/* this file is part of atril, a mate document viewer * * Copyright (C) 2014 Avishkar Gupta * * Author: * Avishkar Gupta <avishkar.gupta.delhi@gmail.com> * * Atril 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. * * Atril 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 "ev-file-helpers.h" #include "epub-document.h" #include "unzip.h" #include "ev-document-thumbnails.h" #include "ev-document-misc.h" #include <libxml/parser.h> #include <libxml/xmlmemory.h> #include <config.h> #include <glib/gi18n.h> #include <glib/gstdio.h> #if GTK_CHECK_VERSION(3, 0, 0) #include <webkit2/webkit2.h> #else #include <webkit/webkit.h> #endif #include <gtk/gtk.h> typedef enum _xmlParseReturnType { XML_ATTRIBUTE, XML_KEYWORD }xmlParseReturnType; typedef struct _contentListNode { gchar* key ; gchar* value ; gint index ; }contentListNode; typedef struct _EpubDocumentClass EpubDocumentClass; struct _EpubDocumentClass { EvDocumentClass parent_class; }; struct _EpubDocument { EvDocument parent_instance; /*Stores the path to the source archive*/ gchar* archivename ; /*Stores the path of the directory where we unzipped the epub*/ gchar* tmp_archive_dir ; /*Stores the contentlist in a sorted manner*/ GList* contentList ; /* A variable to hold our epubDocument for unzipping*/ unzFile epubDocument ; /* A pointer to an offscreen WebKitWebView, for thumbnails*/ WebKitWebView *webview; }; static void epub_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface); EV_BACKEND_REGISTER_WITH_CODE (EpubDocument, epub_document, { EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS, epub_document_document_thumbnails_iface_init); } ); /* A cairo surface for the thumbnails, this probably dosen't need to be global, but I couldn't find a better solution.*/ static cairo_surface_t* surface = NULL ; static gboolean completed = FALSE; static GdkPixbuf *thumbnail=NULL ; static void epub_webkit_render(EpubDocument *document,const char* uri); static GdkPixbuf * epub_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document, EvRenderContext *rc, gboolean border) { gchar* uri = (gchar*) rc->page->backend_page; EpubDocument *epub_document = EPUB_DOCUMENT(document); completed = FALSE ; thumbnail=NULL; if (surface) { cairo_surface_destroy (surface); surface=NULL; } epub_webkit_render (epub_document,uri); while (completed != TRUE ) { /*Wait for the job to complete*/ } if (thumbnail) { return thumbnail; } else { return NULL; } } static void epub_document_thumbnails_get_dimensions (EvDocumentThumbnails *document, EvRenderContext *rc, gint *width, gint *height) { gdouble page_width, page_height; page_width = 800; page_height = 600; *width = MAX ((gint)(page_width * rc->scale + 0.5), 1); *height = MAX ((gint)(page_height * rc->scale + 0.5), 1); } static void epub_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface) { iface->get_thumbnail = epub_document_thumbnails_get_thumbnail; iface->get_dimensions = epub_document_thumbnails_get_dimensions; } static gboolean epub_document_save (EvDocument *document, const char *uri, GError **error) { EpubDocument *epub_document = EPUB_DOCUMENT (document); return ev_xfer_uri_simple (epub_document->archivename, uri, error); } static int epub_document_get_n_pages (EvDocument *document) { EpubDocument *epub_document = EPUB_DOCUMENT (document); if (epub_document-> contentList == NULL) return 0; return g_list_length(epub_document->contentList); } #if !GTK_CHECK_VERSION(3, 0, 0) static void webkit_render_cb(GtkWidget *web_view, GParamSpec *specification, gpointer data) { WebKitLoadStatus status = webkit_web_view_get_load_status (WEBKIT_WEB_VIEW(web_view)); if ( status == WEBKIT_LOAD_FINISHED ) { surface = webkit_web_view_get_snapshot (WEBKIT_WEB_VIEW(web_view)); thumbnail = ev_document_misc_pixbuf_from_surface(surface); completed=TRUE; } } static void epub_webkit_render(EpubDocument *epub_document,const char* uri) { webkit_web_view_load_uri(WEBKIT_WEB_VIEW(epub_document->webview),uri); } static WebKitWebView* offscreen_webview_init() { GtkWidget *offscreen_window = gtk_offscreen_window_new(); gtk_window_set_default_size(GTK_WINDOW(offscreen_window),800,600); GtkWidget* scroll_view = gtk_scrolled_window_new (NULL,NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scroll_view),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); GtkWidget* web_view = webkit_web_view_new (); WebKitWebSettings *webviewsettings = webkit_web_settings_new (); g_object_set (G_OBJECT(webviewsettings), "enable-plugins", FALSE, NULL); webkit_web_view_set_settings (WEBKIT_WEB_VIEW(web_view),webviewsettings); g_signal_connect(WEBKIT_WEB_VIEW(web_view),"notify::load-status",G_CALLBACK(webkit_render_cb),NULL); gtk_container_add(GTK_CONTAINER(scroll_view),web_view); gtk_container_add(GTK_CONTAINER(offscreen_window),scroll_view); gtk_widget_show_all (offscreen_window); return WEBKIT_WEB_VIEW(web_view); } #else /* The webkit2 code for GTK3 */ static void snapshot_chain_cb(WebKitWebView *web_view, GAsyncResult* res, cairo_surface_t **surface) { GError * err = NULL ; *surface = webkit_web_view_get_snapshot_finish(WEBKIT_WEB_VIEW(web_view),res,&err); if ( err ) { surface = NULL ; } } static void webkit_render_cb(WebKitWebView *webview, WebKitLoadEvent load_status, cairo_surface_t **surface) { if ( load_status != WEBKIT_LOAD_FINISHED ) return ; webkit_web_view_get_snapshot(webview, WEBKIT_SNAPSHOT_REGION_FULL_DOCUMENT, WEBKIT_SNAPSHOT_OPTIONS_INCLUDE_SELECTION_HIGHLIGHTING, NULL, (GAsyncReadyCallback)snapshot_chain_cb, surface); } { GtkWidget *offscreen_window = gtk_offscreen_window_new (); gtk_window_set_default_size(GTK_WINDOW(offscreen_window),800,600); GtkWidget* scroll_view = gtk_scrolled_window_new (NULL,NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scroll_view),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); GtkWidget* web_view = webkit_web_view_new (); gtk_container_add(GTK_CONTAINER(offscreen_window),scroll_view); gtk_container_add(GTK_CONTAINER(scroll_view),web_view); gtk_widget_show_all(offscreen_window); g_signal_connect(web_view,"load-changed",G_CALLBACK(webkit_render_cb),surface); return web_view ; } #endif /** * epub_remove_temporary_dir : Removes a directory recursively. * This function is same as comics_remove_temporary_dir * Returns: * 0 if it was successfully deleted, * -1 if an error occurred */ static int epub_remove_temporary_dir (gchar *path_name) { GDir *content_dir; const gchar *filename; gchar *filename_with_path; if (g_file_test (path_name, G_FILE_TEST_IS_DIR)) { content_dir = g_dir_open (path_name, 0, NULL); filename = g_dir_read_name (content_dir); while (filename) { filename_with_path = g_build_filename (path_name, filename, NULL); epub_remove_temporary_dir (filename_with_path); g_free (filename_with_path); filename = g_dir_read_name (content_dir); } g_dir_close (content_dir); } /* Note from g_remove() documentation: on Windows, it is in general not * possible to remove a file that is open to some process, or mapped * into memory.*/ return (g_remove (path_name)); } static gboolean check_mime_type (const gchar* uri, GError** error); static gboolean open_xml_document (const gchar* filename); static gboolean set_xml_root_node (xmlChar* rootname); static xmlNodePtr xml_get_pointer_to_node (xmlChar* parserfor, xmlChar* attributename, xmlChar* attributevalue); static void xml_parse_children_of_node (xmlNodePtr parent, xmlChar* parserfor, xmlChar* attributename, xmlChar* attributevalue); static gboolean xml_check_attribute_value (xmlNode* node, xmlChar * attributename, xmlChar* attributevalue); static xmlChar* xml_get_data_from_node (xmlNodePtr node, xmlParseReturnType rettype, xmlChar* attributename); static void xml_free_doc(); static void free_tree_nodes (gpointer data); /*Global variables for XML parsing*/ static xmlDocPtr xmldocument ; static xmlNodePtr xmlroot ; static xmlNodePtr xmlretval ; /* **Functions to parse the xml files. **Open a XML document for reading */ static gboolean open_xml_document ( const gchar* filename ) { xmldocument = xmlParseFile(filename); if ( xmldocument == NULL ) { return FALSE ; } else { return TRUE ; } } /** *Check if the root value is same as rootname . *if supplied rootvalue = NULL ,just set root to rootnode . **/ static gboolean set_xml_root_node(xmlChar* rootname) { xmlroot = xmlDocGetRootElement(xmldocument); if (xmlroot == NULL) { xmlFreeDoc(xmldocument); return FALSE; } if ( rootname == NULL ) { return TRUE ; } if ( !xmlStrcmp(xmlroot->name,rootname)) { return TRUE ; } else { return FALSE; } } static xmlNodePtr xml_get_pointer_to_node(xmlChar* parserfor, xmlChar* attributename, xmlChar* attributevalue ) { xmlNodePtr topchild,children ; xmlretval = NULL ; if ( !xmlStrcmp( xmlroot->name, parserfor) ) { return xmlroot ; } topchild = xmlroot->xmlChildrenNode ; while ( topchild != NULL ) { if ( !xmlStrcmp(topchild->name,parserfor) ) { if ( xml_check_attribute_value(topchild,attributename,attributevalue) == TRUE ) { xmlretval = topchild; return xmlretval; } else { /*No need to parse children node*/ topchild = topchild->next ; continue ; } } xml_parse_children_of_node(topchild , parserfor, attributename, attributevalue) ; topchild = topchild->next ; } return xmlretval ; } static void xml_parse_children_of_node(xmlNodePtr parent, xmlChar* parserfor, xmlChar* attributename, xmlChar* attributevalue ) { xmlNodePtr child = parent->xmlChildrenNode ; while ( child != NULL ) { if ( !xmlStrcmp(child->name,parserfor)) { if ( xml_check_attribute_value(child,attributename,attributevalue) == TRUE ) { xmlretval = child; return ; } else { /*No need to parse children node*/ child = child->next ; continue ; } } /*return already if we have xmlretval set*/ if ( xmlretval != NULL ) { return ; } xml_parse_children_of_node(child,parserfor,attributename,attributevalue) ; child = child->next ; } } static void xml_free_doc() { xmlFreeDoc(xmldocument); } static gboolean xml_check_attribute_value(xmlNode* node, xmlChar * attributename, xmlChar* attributevalue) { xmlChar* attributefromfile ; if ( attributename == NULL || attributevalue == NULL ) { return TRUE ; } else if ( !xmlStrcmp(( attributefromfile = xmlGetProp(node,attributename)), attributevalue) ) { xmlFree(attributefromfile); return TRUE ; } xmlFree(attributefromfile); return FALSE ; } static xmlChar* xml_get_data_from_node(xmlNodePtr node, xmlParseReturnType rettype, xmlChar* attributename) { xmlChar* datastring ; if ( rettype == XML_ATTRIBUTE ) datastring= xmlGetProp(node,attributename); else datastring= xmlNodeListGetString(xmldocument,node->xmlChildrenNode, 1); return datastring; } static gboolean check_mime_type(const gchar* uri,GError** error) { GError * err = NULL ; gchar* mimeFromFile = ev_file_get_mime_type(uri,FALSE,&err); if ( !mimeFromFile ) { if (err) { g_propagate_error (error, err); } else { g_set_error_literal (error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_INVALID, _("Unknown MIME Type")); } return FALSE; } else if ( g_strcmp0(mimeFromFile, "application/epub+zip") == 0 ) { return TRUE ; } else { g_set_error_literal (error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_INVALID, _("Not an ePub document")); return FALSE; } } static gboolean extract_one_file(EpubDocument* epub_document,GError ** error) { GFile * outfile ; gsize writesize = 0; GString * gfilepath ; unz_file_info64 info ; gchar* directory; GString* dir_create; GFileOutputStream * outstream ; gpointer currentfilename = g_malloc0(512); gpointer buffer = g_malloc0(512); gchar* createdirnametemp = NULL ; gchar* createdirname = NULL; if ( unzOpenCurrentFile(epub_document->epubDocument) != UNZ_OK ) { return FALSE ; } unzGetCurrentFileInfo64(epub_document->epubDocument,&info,currentfilename,512,NULL,0,NULL,0) ; directory = g_strrstr(currentfilename,"/") ; if ( directory != NULL ) directory++; gfilepath = g_string_new(epub_document->tmp_archive_dir) ; g_string_append_printf(gfilepath,"/%s",(gchar*)currentfilename); /*if we encounter a directory, make a directory inside our temporary folder.*/ if (directory != NULL && *directory == '\0') { g_mkdir(gfilepath->str,0777); unzCloseCurrentFile (epub_document->epubDocument) ; g_string_free(gfilepath,TRUE); g_free(currentfilename); g_free(buffer); return TRUE; } else if (directory != NULL && *directory != '\0' ) { gchar* createdir = currentfilename; /*Since a substring can't be longer than the parent string, allocating space equal to the parent's size should suffice*/ createdirname = g_malloc0(strlen(currentfilename)); /* Add the name of the directory and subdiectories,if any to a buffer and then create it */ createdirnametemp = createdirname; while ( createdir != directory ) { (*createdirnametemp) = (*createdir); createdirnametemp++; createdir++; } (*createdirnametemp) = '\0'; dir_create = g_string_new(epub_document->tmp_archive_dir); g_string_append_printf(dir_create,"/%s",createdirname); g_mkdir_with_parents(dir_create->str,0777); g_string_free(dir_create,TRUE); } outfile = g_file_new_for_path(gfilepath->str); outstream = g_file_create(outfile,G_FILE_CREATE_PRIVATE,NULL,error); while ( (writesize = unzReadCurrentFile(epub_document->epubDocument,buffer,512) ) != 0 ) { if ( g_output_stream_write((GOutputStream*)outstream,buffer,writesize,NULL,error) == -1 ) { return FALSE ; } } g_output_stream_close((GOutputStream*)outstream,NULL,error); g_object_unref(outfile) ; g_object_unref(outstream) ; unzCloseCurrentFile (epub_document->epubDocument) ; g_string_free(gfilepath,TRUE); g_free(currentfilename); g_free(buffer); if ( createdirname != NULL) { g_free(createdirname); } return TRUE; } static gboolean extract_epub_from_container (const gchar* uri, EpubDocument *epub_document, GError ** error) { GError* err = NULL ; GString * temporary_sub_directory ; epub_document->archivename = g_filename_from_uri(uri,NULL,error); gchar* epubfilename ; if ( !epub_document->archivename ) { if (err) { g_propagate_error (error, err); } else { g_set_error_literal (error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_INVALID, _("could not retrieve filename")); } return FALSE ; } epubfilename = g_strrstr(epub_document->archivename,"/"); if ( *epubfilename == '/' ) epubfilename++ ; temporary_sub_directory = g_string_new( epubfilename ); g_string_append(temporary_sub_directory,"XXXXXX") ; epub_document->tmp_archive_dir = ev_mkdtemp(temporary_sub_directory->str,error) ; if (!epub_document->tmp_archive_dir) { return FALSE ; } g_string_free(temporary_sub_directory,TRUE); epub_document->epubDocument = unzOpen64(epub_document->archivename); if ( epub_document->epubDocument == NULL ) { if (err) { g_propagate_error (error, err); } else { g_set_error_literal (error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_INVALID, _("could not open archive")); } return FALSE ; } if ( unzGoToFirstFile(epub_document->epubDocument) != UNZ_OK ) { if (err) { g_propagate_error (error, err); } else { g_set_error_literal (error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_INVALID, _("could not extract archive")); } return FALSE ; } while ( TRUE ) { if ( extract_one_file(epub_document,&err) == FALSE ) { if (err) { g_propagate_error (error, err); } else { g_set_error_literal (error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_INVALID, _("could not extract archive")); } return FALSE; } if ( unzGoToNextFile(epub_document->epubDocument) == UNZ_END_OF_LIST_OF_FILE ) break ; } unzClose(epub_document->epubDocument); return TRUE ; } static gchar* get_uri_to_content(const gchar* uri,GError ** error,gchar* tmp_archive_dir) { GError * err = NULL ; gchar* containerpath = g_filename_from_uri(uri,NULL,&err); GString* absolutepath ; gchar* content_uri ; xmlNodePtr rootfileNode ; xmlChar* relativepath; if ( !containerpath ) { if (err) { g_propagate_error (error,err); } else { g_set_error_literal (error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_INVALID, _("could not retrieve container file")); } return NULL ; } if ( open_xml_document(containerpath) == FALSE ) { g_set_error_literal(error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_INVALID, _("could not open container file")); return NULL ; } if ( set_xml_root_node((xmlChar*)"container") == FALSE) { g_set_error_literal(error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_INVALID, _("container file is corrupt")); return NULL ; } if ( (rootfileNode = xml_get_pointer_to_node((xmlChar*)"rootfile",(xmlChar*)"media-type",(xmlChar*)"application/oebps-package+xml")) == NULL) { g_set_error_literal(error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_INVALID, _("epub file is invalid or corrput")); return NULL ; } relativepath = xml_get_data_from_node(rootfileNode,XML_ATTRIBUTE,(xmlChar*)"full-path") ; if ( relativepath == NULL ) { g_set_error_literal(error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_INVALID, _("epub file is corrupt,no container")); return NULL ; } absolutepath = g_string_new(tmp_archive_dir); g_string_append_printf(absolutepath,"/%s",relativepath); content_uri = g_filename_to_uri(absolutepath->str,NULL,&err); if ( !content_uri ) { if (err) { g_propagate_error (error,err); } else { g_set_error_literal (error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_INVALID, _("could not retrieve container file")); } return NULL ; } g_string_free(absolutepath,TRUE); return content_uri ; } static GList* setup_document_content_list(const gchar* content_uri, GError** error,gchar *tmp_archive_dir) { GList* newlist = NULL ; GError * err = NULL ; gint indexcounter= 1; xmlNodePtr manifest,spine,itemrefptr,itemptr ; gboolean errorflag = FALSE; gchar* relativepath ; GString* absolutepath = g_string_new(NULL); if ( open_xml_document(content_uri) == FALSE ) { g_set_error_literal(error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_INVALID, _("could not parse content manifest")); return FALSE ; } if ( set_xml_root_node((xmlChar*)"package") == FALSE) { g_set_error_literal(error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_INVALID, _("content file is invalid")); return FALSE ; } if ( ( spine = xml_get_pointer_to_node((xmlChar*)"spine",NULL,NULL) )== NULL ) { g_set_error_literal(error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_INVALID, _("epub file has no spine")); return FALSE ; } if ( ( manifest = xml_get_pointer_to_node((xmlChar*)"manifest",NULL,NULL) )== NULL ) { g_set_error_literal(error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_INVALID, _("epub file has no manifest")); return FALSE ; } xmlretval = NULL ; /*Get first instance of itemref from the spine*/ xml_parse_children_of_node(spine,(xmlChar*)"itemref",NULL,NULL); if ( xmlretval != NULL ) itemrefptr = xmlretval ; else { errorflag=TRUE; } /*Parse the spine for remaining itemrefs*/ do { /*for the first time that we enter the loop, if errorflag is set we break*/ if ( errorflag ) { break; } if ( xmlStrcmp(itemrefptr->name,(xmlChar*)"itemref") == 0) { contentListNode* newnode = g_malloc0(sizeof(newnode)); newnode->key = (gchar*)xml_get_data_from_node(itemrefptr,XML_ATTRIBUTE,(xmlChar*)"idref"); if ( newnode->key == NULL ) { errorflag =TRUE; break; } xmlretval=NULL ; xml_parse_children_of_node(manifest,(xmlChar*)"item",(xmlChar*)"id",(xmlChar*)newnode->key); if ( xmlretval != NULL ) { itemptr = xmlretval ; } else { errorflag=TRUE; break; } relativepath = (gchar*)xml_get_data_from_node(itemptr,XML_ATTRIBUTE,(xmlChar*)"href"); g_string_assign(absolutepath,tmp_archive_dir); g_string_append_printf(absolutepath,"/%s",relativepath); newnode->value = g_filename_to_uri(absolutepath->str,NULL,&err); if ( newnode->value == NULL ) { errorflag =TRUE; break; } newnode->index = indexcounter++ ; newlist = g_list_prepend(newlist,newnode); } itemrefptr = itemrefptr->next ; } while ( itemrefptr != NULL ); if ( errorflag ) { if ( err ) { g_propagate_error(error,err); } else { g_set_error_literal(error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_INVALID, _("Could not set up document tree for loading, some files missing")); } /*free any nodes that were set up and return empty*/ g_string_free(absolutepath,TRUE); g_list_free_full(newlist,(GDestroyNotify)free_tree_nodes); return NULL ; } newlist = g_list_reverse(newlist); g_string_free(absolutepath,TRUE); return newlist ; } /* Callback function to free the contentlist.*/ static void free_tree_nodes(gpointer data) { contentListNode* dataptr = data ; g_free(dataptr->value); g_free(dataptr->key); g_free(dataptr); } static void epub_document_init (EpubDocument *epub_document) { epub_document->archivename = NULL ; epub_document->tmp_archive_dir = NULL ; epub_document->contentList = NULL ; epub_document->webview = offscreen_webview_init(); } static gboolean epub_document_load (EvDocument* document, const char* uri, GError** error) { EpubDocument *epub_document = EPUB_DOCUMENT(document); GError* err = NULL ; gchar* containeruri ; GString *containerpath ; gchar* contentOpfUri ; if ( check_mime_type (uri,&err) == FALSE ) { /*Error would've been set by the function*/ g_propagate_error(error,err); return FALSE; } extract_epub_from_container (uri,epub_document,&err); if ( err ) { g_propagate_error( error,err ); return FALSE; } /*FIXME : can this be different, ever?*/ containerpath = g_string_new(epub_document->tmp_archive_dir); g_string_append_printf(containerpath,"/META-INF/container.xml"); containeruri = g_filename_to_uri(containerpath->str,NULL,&err); if ( err ) { g_propagate_error(error,err); return FALSE; } contentOpfUri = get_uri_to_content (containeruri,&err,epub_document->tmp_archive_dir); if ( contentOpfUri == NULL ) { g_propagate_error(error,err); return FALSE; } xml_free_doc() ; epub_document->contentList = setup_document_content_list (contentOpfUri,&err,epub_document->tmp_archive_dir); if ( xmldocument != NULL ) xml_free_doc (); if ( epub_document->contentList == NULL ) { g_propagate_error(error,err); return FALSE; } return TRUE ; } static void epub_document_finalize (GObject *object) { EpubDocument *epub_document = EPUB_DOCUMENT (object); if (epub_document->epubDocument != NULL) { if (epub_remove_temporary_dir (epub_document->tmp_archive_dir) == -1) g_warning (_("There was an error deleting ā%sā."), epub_document->tmp_archive_dir); g_free (epub_document->tmp_archive_dir); } if ( epub_document->contentList ) { g_list_free_full(epub_document->contentList,(GDestroyNotify)free_tree_nodes); } if ( epub_document->tmp_archive_dir) { g_free (epub_document->tmp_archive_dir); } if ( epub_document->archivename) { g_free (epub_document->archivename); } G_OBJECT_CLASS (epub_document_parent_class)->finalize (object); } static EvDocumentInfo* epub_document_get_info(EvDocument *document) { EpubDocument *epub_document = EPUB_DOCUMENT(document); GError *error = NULL ; gchar* infofile ; xmlNodePtr metanode ; GString* buffer ; gchar* archive_dir = epub_document->tmp_archive_dir; GString* containerpath = g_string_new(epub_document->tmp_archive_dir); g_string_append_printf(containerpath,"/META-INF/container.xml"); gchar* containeruri = g_filename_to_uri(containerpath->str,NULL,&error); if ( error ) { return NULL ; } gchar* uri = get_uri_to_content (containeruri,&error,archive_dir); if ( error ) { return NULL ; } EvDocumentInfo* epubinfo = g_new0 (EvDocumentInfo, 1); if ( xmldocument != NULL ) xml_free_doc(); infofile = g_filename_from_uri(uri,NULL,&error); if ( error ) return epubinfo; open_xml_document(infofile); set_xml_root_node((xmlChar*)"package"); metanode = xml_get_pointer_to_node((xmlChar*)"title",NULL,NULL); if ( metanode == NULL ) epubinfo->title = NULL ; else epubinfo->title = (char*)xml_get_data_from_node(metanode,XML_KEYWORD,NULL); metanode = xml_get_pointer_to_node((xmlChar*)"creator",NULL,NULL); if ( metanode == NULL ) epubinfo->author = g_strdup("unknown"); else epubinfo->author = (char*)xml_get_data_from_node(metanode,XML_KEYWORD,NULL); metanode = xml_get_pointer_to_node((xmlChar*)"subject",NULL,NULL); if ( metanode == NULL ) epubinfo->subject = g_strdup("unknown"); else epubinfo->subject = (char*)xml_get_data_from_node(metanode,XML_KEYWORD,NULL); buffer = g_string_new((gchar*)xml_get_data_from_node (xmlroot,XML_ATTRIBUTE,(xmlChar*)"version")); g_string_prepend(buffer,"epub "); epubinfo->format = g_strdup(buffer->str); /*FIXME: Add more of these as you write the corresponding modules*/ epubinfo->permissions = EV_DOCUMENT_PERMISSIONS_OK_TO_ADD_NOTES; epubinfo->layout = EV_DOCUMENT_LAYOUT_SINGLE_PAGE; metanode = xml_get_pointer_to_node((xmlChar*)"publisher",NULL,NULL); if ( metanode == NULL ) epubinfo->creator = g_strdup("unknown"); else epubinfo->creator = (char*)xml_get_data_from_node(metanode,XML_KEYWORD,NULL); /*TODO : Add a function to get date*/ g_free(uri); g_string_free(containerpath,TRUE); g_string_free(buffer,TRUE); return epubinfo ; } static EvPage* epub_document_get_page(EvDocument *document, gint index) { EpubDocument *epub_document = EPUB_DOCUMENT(document); EvPage* page = ev_page_new(index); contentListNode *listptr = g_list_nth_data (epub_document->contentList,index); page->backend_page = g_strdup(listptr->value); return page ; } static void epub_document_class_init (EpubDocumentClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass); gobject_class->finalize = epub_document_finalize; ev_document_class->load = epub_document_load; ev_document_class->save = epub_document_save; ev_document_class->get_n_pages = epub_document_get_n_pages; ev_document_class->get_info = epub_document_get_info; ev_document_class->get_page = epub_document_get_page; }