1 /* GepubDoc
2  *
3  * Copyright (C) 2011 Daniel Garcia <danigm@wadobo.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #include <config.h>
21 #include <libxml/tree.h>
22 #include <libxml/HTMLparser.h>
23 #include <string.h>
24 
25 #include "gepub-utils.h"
26 #include "gepub-doc.h"
27 #include "gepub-archive.h"
28 #include "gepub-text-chunk.h"
29 
30 
31 static GQuark
gepub_error_quark(void)32 gepub_error_quark (void)
33 {
34     static GQuark q = 0;
35     if (q == 0)
36         q = g_quark_from_string ("gepub-quark");
37     return q;
38 }
39 
40 /**
41  * GepubDocError:
42  * @GEPUB_ERROR_INVALID: Invalid file
43  *
44  * Common errors that may be reported by GepubDoc.
45  */
46 typedef enum {
47     GEPUB_ERROR_INVALID = 0,  /*< nick=Invalid >*/
48 } GepubDocError;
49 
50 
51 
52 static void gepub_doc_fill_resources (GepubDoc *doc);
53 static void gepub_doc_fill_spine (GepubDoc *doc);
54 static void gepub_doc_initable_iface_init (GInitableIface *iface);
55 
56 struct _GepubDoc {
57     GObject parent;
58 
59     GepubArchive *archive;
60     GBytes *content;
61     gchar *content_base;
62     gchar *path;
63     GHashTable *resources;
64 
65     GList *spine;
66     GList *chapter;
67 };
68 
69 struct _GepubDocClass {
70     GObjectClass parent_class;
71 };
72 
73 enum {
74     PROP_0,
75     PROP_PATH,
76     PROP_CHAPTER,
77     NUM_PROPS
78 };
79 
80 static GParamSpec *properties[NUM_PROPS] = { NULL, };
81 
G_DEFINE_TYPE_WITH_CODE(GepubDoc,gepub_doc,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,gepub_doc_initable_iface_init))82 G_DEFINE_TYPE_WITH_CODE (GepubDoc, gepub_doc, G_TYPE_OBJECT,
83                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gepub_doc_initable_iface_init))
84 
85 static void
86 gepub_resource_free (GepubResource *res)
87 {
88     g_free (res->mime);
89     g_free (res->uri);
90     g_free (res);
91 }
92 
93 static void
gepub_doc_finalize(GObject * object)94 gepub_doc_finalize (GObject *object)
95 {
96     GepubDoc *doc = GEPUB_DOC (object);
97 
98     g_clear_object (&doc->archive);
99     g_clear_pointer (&doc->content, g_bytes_unref);
100     g_clear_pointer (&doc->path, g_free);
101     g_clear_pointer (&doc->resources, g_hash_table_destroy);
102 
103     if (doc->spine) {
104         g_list_foreach (doc->spine, (GFunc)g_free, NULL);
105         g_clear_pointer (&doc->spine, g_list_free);
106     }
107 
108     G_OBJECT_CLASS (gepub_doc_parent_class)->finalize (object);
109 }
110 
111 static void
gepub_doc_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)112 gepub_doc_set_property (GObject      *object,
113 			guint         prop_id,
114 			const GValue *value,
115 			GParamSpec   *pspec)
116 {
117     GepubDoc *doc = GEPUB_DOC (object);
118 
119     switch (prop_id) {
120     case PROP_PATH:
121         doc->path = g_value_dup_string (value);
122         break;
123     case PROP_CHAPTER:
124         gepub_doc_set_chapter (doc, g_value_get_int (value));
125         break;
126     default:
127         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
128         break;
129     }
130 }
131 
132 static void
gepub_doc_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)133 gepub_doc_get_property (GObject    *object,
134 			guint       prop_id,
135 			GValue     *value,
136 			GParamSpec *pspec)
137 {
138     GepubDoc *doc = GEPUB_DOC (object);
139 
140     switch (prop_id) {
141     case PROP_PATH:
142         g_value_set_string (value, doc->path);
143         break;
144     case PROP_CHAPTER:
145         g_value_set_int (value, gepub_doc_get_chapter (doc));
146         break;
147     default:
148         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
149         break;
150     }
151 }
152 
153 static void
gepub_doc_init(GepubDoc * doc)154 gepub_doc_init (GepubDoc *doc)
155 {
156     /* doc resources hashtable:
157      * id : (mime, path)
158      */
159     doc->resources = g_hash_table_new_full (g_str_hash,
160                                             g_str_equal,
161                                             (GDestroyNotify)g_free,
162                                             (GDestroyNotify)gepub_resource_free);
163 }
164 
165 static void
gepub_doc_class_init(GepubDocClass * klass)166 gepub_doc_class_init (GepubDocClass *klass)
167 {
168     GObjectClass *object_class = G_OBJECT_CLASS (klass);
169 
170     object_class->finalize = gepub_doc_finalize;
171     object_class->set_property = gepub_doc_set_property;
172     object_class->get_property = gepub_doc_get_property;
173 
174     properties[PROP_PATH] =
175         g_param_spec_string ("path",
176                              "Path",
177                              "Path to the EPUB document",
178                              NULL,
179                              G_PARAM_READWRITE |
180                              G_PARAM_CONSTRUCT_ONLY |
181                              G_PARAM_STATIC_STRINGS);
182     properties[PROP_CHAPTER] =
183         g_param_spec_int ("chapter",
184                           "Current chapter",
185                           "The current chapter index",
186                           -1, G_MAXINT, 0,
187                           G_PARAM_READWRITE |
188                           G_PARAM_STATIC_STRINGS);
189 
190     g_object_class_install_properties (object_class, NUM_PROPS, properties);
191 }
192 
193 static gboolean
gepub_doc_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)194 gepub_doc_initable_init (GInitable     *initable,
195                          GCancellable  *cancellable,
196                          GError       **error)
197 {
198     GepubDoc *doc = GEPUB_DOC (initable);
199     gchar *file;
200     gint i = 0, len;
201 
202     g_assert (doc->path != NULL);
203 
204     doc->archive = gepub_archive_new (doc->path);
205     file = gepub_archive_get_root_file (doc->archive);
206     if (!file) {
207         if (error != NULL) {
208             g_set_error (error, gepub_error_quark (), GEPUB_ERROR_INVALID,
209                          "Invalid epub file: %s", doc->path);
210         }
211         return FALSE;
212     }
213     doc->content = gepub_archive_read_entry (doc->archive, file);
214     if (!doc->content) {
215         if (error != NULL) {
216             g_set_error (error, gepub_error_quark (), GEPUB_ERROR_INVALID,
217                          "Invalid epub file: %s", doc->path);
218         }
219         return FALSE;
220     }
221 
222     len = strlen (file);
223     doc->content_base = g_strdup ("");
224     for (i=0; i<len; i++) {
225         if (file[i] == '/') {
226             g_free (doc->content_base);
227             doc->content_base = g_strndup (file, i+1);
228             break;
229         }
230     }
231 
232     gepub_doc_fill_resources (doc);
233     gepub_doc_fill_spine (doc);
234 
235     g_free (file);
236 
237     return TRUE;
238 }
239 
240 static void
gepub_doc_initable_iface_init(GInitableIface * iface)241 gepub_doc_initable_iface_init (GInitableIface *iface)
242 {
243     iface->init = gepub_doc_initable_init;
244 }
245 
246 /**
247  * gepub_doc_new:
248  * @path: the epub doc path
249  * @error: (nullable): Error
250  *
251  * Returns: (transfer full): the new GepubDoc created
252  */
253 GepubDoc *
gepub_doc_new(const gchar * path,GError ** error)254 gepub_doc_new (const gchar *path, GError **error)
255 {
256     return g_initable_new (GEPUB_TYPE_DOC,
257                            NULL, error,
258                            "path", path,
259                            NULL);
260 }
261 
262 static void
gepub_doc_fill_resources(GepubDoc * doc)263 gepub_doc_fill_resources (GepubDoc *doc)
264 {
265     xmlDoc *xdoc = NULL;
266     xmlNode *root_element = NULL;
267     xmlNode *mnode = NULL;
268     xmlNode *item = NULL;
269     gchar *id, *tmpuri, *uri;
270     GepubResource *res;
271     const char *data;
272     gsize size;
273 
274     data = g_bytes_get_data (doc->content, &size);
275     xdoc = xmlRecoverMemory (data, size);
276     root_element = xmlDocGetRootElement (xdoc);
277     mnode = gepub_utils_get_element_by_tag (root_element, "manifest");
278 
279     item = mnode->children;
280     while (item) {
281         if (item->type != XML_ELEMENT_NODE ) {
282             item = item->next;
283             continue;
284         }
285 
286         id = gepub_utils_get_prop (item, "id");
287         tmpuri = gepub_utils_get_prop (item, "href");
288         uri = g_strdup_printf ("%s%s", doc->content_base, tmpuri);
289         g_free (tmpuri);
290 
291         res = g_malloc (sizeof (GepubResource));
292         res->mime = gepub_utils_get_prop (item, "media-type");
293         res->uri = uri;
294         g_hash_table_insert (doc->resources, id, res);
295         item = item->next;
296     }
297 
298     xmlFreeDoc (xdoc);
299 }
300 
301 static void
gepub_doc_fill_spine(GepubDoc * doc)302 gepub_doc_fill_spine (GepubDoc *doc)
303 {
304     xmlDoc *xdoc = NULL;
305     xmlNode *root_element = NULL;
306     xmlNode *snode = NULL;
307     xmlNode *item = NULL;
308     gchar *id;
309     const char *data;
310     gsize size;
311     GList *spine = NULL;
312 
313     data = g_bytes_get_data (doc->content, &size);
314     xdoc = xmlRecoverMemory (data, size);
315     root_element = xmlDocGetRootElement (xdoc);
316     snode = gepub_utils_get_element_by_tag (root_element, "spine");
317 
318     item = snode->children;
319     while (item) {
320         if (item->type != XML_ELEMENT_NODE ) {
321             item = item->next;
322             continue;
323         }
324 
325         id = gepub_utils_get_prop (item, "idref");
326 
327         spine = g_list_prepend (spine, id);
328         item = item->next;
329     }
330 
331     doc->spine = g_list_reverse (spine);
332     doc->chapter = doc->spine;
333 
334     xmlFreeDoc (xdoc);
335 }
336 
337 /**
338  * gepub_doc_get_content:
339  * @doc: a #GepubDoc
340  *
341  * Returns: (transfer none): the document content
342  */
343 GBytes *
gepub_doc_get_content(GepubDoc * doc)344 gepub_doc_get_content (GepubDoc *doc)
345 {
346     g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL);
347 
348     return doc->content;
349 }
350 
351 /**
352  * gepub_doc_get_metadata:
353  * @doc: a #GepubDoc
354  * @mdata: a metadata name string, GEPUB_META_TITLE for example
355  *
356  * Returns: (transfer full): metadata string
357  */
358 gchar *
gepub_doc_get_metadata(GepubDoc * doc,const gchar * mdata)359 gepub_doc_get_metadata (GepubDoc *doc, const gchar *mdata)
360 {
361     xmlDoc *xdoc = NULL;
362     xmlNode *root_element = NULL;
363     xmlNode *mnode = NULL;
364     xmlNode *mdata_node = NULL;
365     gchar *ret;
366     xmlChar *text;
367     const char *data;
368     gsize size;
369 
370     g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL);
371     g_return_val_if_fail (mdata != NULL, NULL);
372 
373     data = g_bytes_get_data (doc->content, &size);
374     xdoc = xmlRecoverMemory (data, size);
375     root_element = xmlDocGetRootElement (xdoc);
376     mnode = gepub_utils_get_element_by_tag (root_element, "metadata");
377     mdata_node = gepub_utils_get_element_by_tag (mnode, mdata);
378 
379     text = xmlNodeGetContent (mdata_node);
380     ret = g_strdup ((const char *) text);
381     xmlFree (text);
382 
383     xmlFreeDoc (xdoc);
384 
385     return ret;
386 }
387 
388 /**
389  * gepub_doc_get_resources:
390  * @doc: a #GepubDoc
391  *
392  * Returns: (element-type utf8 Gepub.Resource) (transfer none): doc resource table
393  */
394 GHashTable *
gepub_doc_get_resources(GepubDoc * doc)395 gepub_doc_get_resources (GepubDoc *doc)
396 {
397     g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL);
398 
399     return doc->resources;
400 }
401 
402 /**
403  * gepub_doc_get_resource_by_id:
404  * @doc: a #GepubDoc
405  * @id: the resource id
406  *
407  * Returns: (transfer full): the resource content
408  */
409 GBytes *
gepub_doc_get_resource_by_id(GepubDoc * doc,const gchar * id)410 gepub_doc_get_resource_by_id (GepubDoc *doc, const gchar *id)
411 {
412     GepubResource *gres;
413 
414     g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL);
415     g_return_val_if_fail (id != NULL, NULL);
416 
417     gres = g_hash_table_lookup (doc->resources, id);
418     if (!gres) {
419         // not found
420         return NULL;
421     }
422 
423     return gepub_archive_read_entry (doc->archive, gres->uri);
424 }
425 
426 /**
427  * gepub_doc_get_resource:
428  * @doc: a #GepubDoc
429  * @path: the resource path
430  *
431  * Returns: (transfer full): the resource content
432  */
433 GBytes *
gepub_doc_get_resource(GepubDoc * doc,const gchar * path)434 gepub_doc_get_resource (GepubDoc *doc, const gchar *path)
435 {
436     g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL);
437     g_return_val_if_fail (path != NULL, NULL);
438 
439     return gepub_archive_read_entry (doc->archive, path);
440 }
441 
442 /**
443  * gepub_doc_get_resource_mime_by_id:
444  * @doc: a #GepubDoc
445  * @id: the resource id
446  *
447  * Returns: (transfer full): the resource content
448  */
449 gchar *
gepub_doc_get_resource_mime_by_id(GepubDoc * doc,const gchar * id)450 gepub_doc_get_resource_mime_by_id (GepubDoc *doc, const gchar *id)
451 {
452     GepubResource *gres;
453 
454     g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL);
455     g_return_val_if_fail (id != NULL, NULL);
456 
457     gres = g_hash_table_lookup (doc->resources, id);
458     if (!gres) {
459         // not found
460         return NULL;
461     }
462 
463     return g_strdup (gres->mime);
464 }
465 
466 /**
467  * gepub_doc_get_resource_mime:
468  * @doc: a #GepubDoc
469  * @path: the resource path
470  *
471  * Returns: (transfer full): the resource mime
472  */
473 gchar *
gepub_doc_get_resource_mime(GepubDoc * doc,const gchar * path)474 gepub_doc_get_resource_mime (GepubDoc *doc, const gchar *path)
475 {
476     GepubResource *gres;
477     GList *keys;
478 
479     g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL);
480     g_return_val_if_fail (path != NULL, NULL);
481 
482     keys = g_hash_table_get_keys (doc->resources);
483 
484     while (keys) {
485         gres = ((GepubResource*)g_hash_table_lookup (doc->resources, keys->data));
486         if (!strcmp (gres->uri, path))
487             break;
488         keys = keys->next;
489     }
490 
491     if (keys)
492         return g_strdup (gres->mime);
493     else
494         return NULL;
495 }
496 
497 /**
498  * gepub_doc_get_current_mime:
499  * @doc: a #GepubDoc
500  *
501  * Returns: (transfer full): the current resource mime
502  */
503 gchar *
gepub_doc_get_current_mime(GepubDoc * doc)504 gepub_doc_get_current_mime (GepubDoc *doc)
505 {
506     g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL);
507     g_return_val_if_fail (doc->chapter != NULL, NULL);
508 
509     return gepub_doc_get_resource_mime_by_id (doc, doc->chapter->data);
510 }
511 
512 /**
513  * gepub_doc_get_current:
514  * @doc: a #GepubDoc
515  *
516  * Returns: (transfer full): the current chapter data
517  */
518 GBytes *
gepub_doc_get_current(GepubDoc * doc)519 gepub_doc_get_current (GepubDoc *doc)
520 {
521     g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL);
522     g_return_val_if_fail (doc->chapter != NULL, NULL);
523 
524     return gepub_doc_get_resource_by_id (doc, doc->chapter->data);
525 }
526 
527 /**
528  * gepub_doc_get_current_with_epub_uris:
529  * @doc: a #GepubDoc
530  *
531  * Returns: (transfer full): the current chapter
532  * data, with resource uris renamed so they have the epub:/// prefix and all
533  * are relative to the root file
534  */
535 GBytes *
gepub_doc_get_current_with_epub_uris(GepubDoc * doc)536 gepub_doc_get_current_with_epub_uris (GepubDoc *doc)
537 {
538     GBytes *content, *replaced;
539     gchar *path, *base;
540 
541     g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL);
542 
543     content = gepub_doc_get_current (doc);
544     path = gepub_doc_get_current_path (doc);
545     // getting the basepath of the current xhtml loaded
546     base = g_path_get_dirname (path);
547 
548     replaced = gepub_utils_replace_resources (content, base);
549 
550     g_free (path);
551     g_bytes_unref (content);
552 
553     return replaced;
554 }
555 
556 /**
557  * gepub_doc_get_text:
558  * @doc: a #GepubDoc
559  *
560  * Returns: (element-type Gepub.TextChunk) (transfer full): the list of text in the current chapter.
561  */
562 GList *
gepub_doc_get_text(GepubDoc * doc)563 gepub_doc_get_text (GepubDoc *doc)
564 {
565     xmlDoc *xdoc = NULL;
566     xmlNode *root_element = NULL;
567     GBytes *current;
568     const gchar *data;
569     gsize size;
570 
571     GList *texts = NULL;
572 
573     g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL);
574 
575     current = gepub_doc_get_current (doc);
576     if (!current) {
577         return NULL;
578     }
579     data = g_bytes_get_data (current, &size);
580     xdoc = htmlReadMemory (data, size, "", NULL, HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR);
581     root_element = xmlDocGetRootElement (xdoc);
582     texts = gepub_utils_get_text_elements (root_element);
583 
584     g_bytes_unref (current);
585     xmlFreeDoc (xdoc);
586 
587     return texts;
588 }
589 
590 /**
591  * gepub_doc_get_text_by_id:
592  * @doc: a #GepubDoc
593  * @id: the resource id
594  *
595  * Returns: (element-type Gepub.TextChunk) (transfer full): the list of text in the current chapter.
596  */
597 GList *
gepub_doc_get_text_by_id(GepubDoc * doc,const gchar * id)598 gepub_doc_get_text_by_id (GepubDoc *doc, const gchar *id)
599 {
600     xmlDoc *xdoc = NULL;
601     xmlNode *root_element = NULL;
602     gsize size;
603     const gchar *res;
604     GBytes *contents;
605 
606     GList *texts = NULL;
607 
608     g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL);
609     g_return_val_if_fail (id != NULL, NULL);
610 
611     contents = gepub_doc_get_resource_by_id (doc, id);
612     if (!contents) {
613         return NULL;
614     }
615 
616     res = g_bytes_get_data (contents, &size);
617     xdoc = htmlReadMemory (res, size, "", NULL, HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR);
618     root_element = xmlDocGetRootElement (xdoc);
619     texts = gepub_utils_get_text_elements (root_element);
620 
621     g_bytes_unref (contents);
622     xmlFreeDoc (xdoc);
623 
624     return texts;
625 }
626 
627 static gboolean
gepub_doc_set_chapter_internal(GepubDoc * doc,GList * chapter)628 gepub_doc_set_chapter_internal (GepubDoc *doc,
629                                 GList    *chapter)
630 {
631     if (!chapter || doc->chapter == chapter)
632         return FALSE;
633 
634     doc->chapter = chapter;
635     g_object_notify_by_pspec (G_OBJECT (doc), properties[PROP_CHAPTER]);
636 
637     return TRUE;
638 }
639 
640 /**
641  * gepub_doc_go_next:
642  * @doc: a #GepubDoc
643  *
644  * Returns: TRUE on success, FALSE if there's no next chapter
645  */
646 gboolean
gepub_doc_go_next(GepubDoc * doc)647 gepub_doc_go_next (GepubDoc *doc)
648 {
649     g_return_val_if_fail (GEPUB_IS_DOC (doc), FALSE);
650     g_return_val_if_fail (doc->chapter != NULL, FALSE);
651 
652     return gepub_doc_set_chapter_internal (doc, doc->chapter->next);
653 }
654 
655 /**
656  * gepub_doc_go_prev:
657  * @doc: a #GepubDoc
658  *
659  * Returns: TRUE on success, FALSE if there's no previous chapter
660  */
661 gboolean
gepub_doc_go_prev(GepubDoc * doc)662 gepub_doc_go_prev (GepubDoc *doc)
663 {
664     g_return_val_if_fail (GEPUB_IS_DOC (doc), FALSE);
665     g_return_val_if_fail (doc->chapter != NULL, FALSE);
666 
667     return gepub_doc_set_chapter_internal (doc, doc->chapter->prev);
668 }
669 
670 /**
671  * gepub_doc_get_n_chapters:
672  * @doc: a #GepubDoc
673  *
674  * Returns: the number of chapters in the document
675  */
676 int
gepub_doc_get_n_chapters(GepubDoc * doc)677 gepub_doc_get_n_chapters (GepubDoc *doc)
678 {
679     g_return_val_if_fail (GEPUB_IS_DOC (doc), 0);
680 
681     return g_list_length (doc->spine);
682 }
683 
684 /**
685  * gepub_doc_get_chapter:
686  * @doc: a #GepubDoc
687  *
688  * Returns: the current chapter index, starting from 0
689  */
690 int
gepub_doc_get_chapter(GepubDoc * doc)691 gepub_doc_get_chapter (GepubDoc *doc)
692 {
693     g_return_val_if_fail (GEPUB_IS_DOC (doc), 0);
694     g_return_val_if_fail (doc->spine != NULL, 0);
695     g_return_val_if_fail (doc->chapter != NULL, 0);
696 
697     return g_list_position (doc->spine, doc->chapter);
698 }
699 
700 /**
701  * gepub_doc_set_chapter:
702  * @doc: a #GepubDoc
703  * @index: the index of the new chapter
704  *
705  * Sets the document current chapter to @index.
706  */
707 void
gepub_doc_set_chapter(GepubDoc * doc,gint index)708 gepub_doc_set_chapter (GepubDoc *doc,
709                     gint      index)
710 {
711     GList *chapter;
712 
713     g_return_if_fail (GEPUB_IS_DOC (doc));
714 
715     g_return_if_fail (index >= 0 && index <= gepub_doc_get_n_chapters (doc));
716 
717     chapter = g_list_nth (doc->spine, index);
718     gepub_doc_set_chapter_internal (doc, chapter);
719 }
720 
721 /**
722  * gepub_doc_get_cover:
723  * @doc: a #GepubDoc
724  *
725  * Returns: (transfer full): cover file path to retrieve with
726  * gepub_doc_get_resource
727  */
728 gchar *
gepub_doc_get_cover(GepubDoc * doc)729 gepub_doc_get_cover (GepubDoc *doc)
730 {
731     xmlDoc *xdoc = NULL;
732     xmlNode *root_element = NULL;
733     xmlNode *mnode = NULL;
734     gchar *ret;
735     const char *data;
736     gsize size;
737 
738     g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL);
739     g_return_val_if_fail (doc->content != NULL, NULL);
740 
741     data = g_bytes_get_data (doc->content, &size);
742     xdoc = xmlRecoverMemory (data, size);
743     root_element = xmlDocGetRootElement (xdoc);
744     mnode = gepub_utils_get_element_by_attr (root_element, "name", "cover");
745     ret = gepub_utils_get_prop (mnode, "content");
746 
747     xmlFreeDoc (xdoc);
748 
749     return ret;
750 }
751 
752 /**
753  * gepub_doc_get_resource_path:
754  * @doc: a #GepubDoc
755  * @id: the resource id
756  *
757  * Returns: (transfer full): the resource path
758  */
759 gchar *
gepub_doc_get_resource_path(GepubDoc * doc,const gchar * id)760 gepub_doc_get_resource_path (GepubDoc *doc, const gchar *id)
761 {
762     GepubResource *gres;
763 
764     g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL);
765     g_return_val_if_fail (id != NULL, NULL);
766 
767     gres = g_hash_table_lookup (doc->resources, id);
768     if (!gres) {
769         // not found
770         return NULL;
771     }
772 
773     return g_strdup (gres->uri);
774 }
775 
776 /**
777  * gepub_doc_get_current_path:
778  * @doc: a #GepubDoc
779  *
780  * Returns: (transfer full): the current resource path
781  */
782 gchar *
gepub_doc_get_current_path(GepubDoc * doc)783 gepub_doc_get_current_path (GepubDoc *doc)
784 {
785     g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL);
786     g_return_val_if_fail (doc->chapter != NULL, NULL);
787 
788     return gepub_doc_get_resource_path (doc, doc->chapter->data);
789 }
790 
791 /**
792  * gepub_doc_get_current_id:
793  * @doc: a #GepubDoc
794  *
795 
796  * Returns: (transfer none): the current resource id
797  */
798 const gchar *
gepub_doc_get_current_id(GepubDoc * doc)799 gepub_doc_get_current_id (GepubDoc *doc)
800 {
801     g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL);
802     g_return_val_if_fail (doc->chapter != NULL, NULL);
803 
804     return doc->chapter->data;
805 }
806