[evince] Add ev_job_load_new_with_data() to load files from data in memory.



commit 5316652b49fe97ef69e51d6a5a0a0cec45a9f41e
Author: Krzesimir Nowak <qdlacz gmail com>
Date:   Thu Apr 26 15:26:55 2012 +0200

    Add ev_job_load_new_with_data() to load files from data in memory.
    
    * libview/ev-jobs.h b/libview/ev-jobs.[h|c]: Add data and data_length struct
    fields. Add ev_job_load_new_with_data() to set them.
    ev_job_load_run(): Call the new ev_document_load_from_data() function instead
    of ev_document_load(), if data is set.
    * libdocument/ev-document-factory.[h|c]:
    Add ev_document_factory_get_document_from_data(), using the same strucure as
    the existing ev_document_factory_get_document(uri). Note that this does not
    yet support compressed data.
    * libdocument/ev-document.[h|c]: Add a load_data() vfunc.
    Add ev_document_load_from_data(), calling that vfunc, like the existing
    ev_document_load().
    * backend/pdf/ev-poppler.cc: Add pdf_document_load_data(), implementing
    the EvDocument load_data vfunc.
    
    Original patch by Murray Cumming <murrayc murrayc com>

 backend/pdf/ev-poppler.cc         |   23 ++++
 libdocument/ev-document-factory.c |  169 +++++++++++++++++++++++++++++++
 libdocument/ev-document-factory.h |    1 +
 libdocument/ev-document.c         |  201 ++++++++++++++++++++++++-------------
 libdocument/ev-document.h         |   11 ++-
 libview/ev-jobs.c                 |   44 +++++++--
 libview/ev-jobs.h                 |    4 +
 7 files changed, 376 insertions(+), 77 deletions(-)
---
diff --git a/backend/pdf/ev-poppler.cc b/backend/pdf/ev-poppler.cc
index 1cbd630..fcd408b 100644
--- a/backend/pdf/ev-poppler.cc
+++ b/backend/pdf/ev-poppler.cc
@@ -280,6 +280,28 @@ pdf_document_load (EvDocument   *document,
 	return TRUE;
 }
 
+static gboolean
+pdf_document_load_data (EvDocument    *document,
+			const guchar  *data,
+			gsize          length,
+			GError       **error)
+{
+	GError *poppler_error = NULL;
+	PdfDocument *pdf_document = PDF_DOCUMENT (document);
+
+	// The cast should not really be necessary.
+	// See https://bugs.freedesktop.org/show_bug.cgi?id=39322
+	pdf_document->document =
+		poppler_document_new_from_data ((char*)data, length, pdf_document->password, &poppler_error);
+
+	if (pdf_document->document == NULL) {
+		convert_error (poppler_error, error);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 static int
 pdf_document_get_n_pages (EvDocument *document)
 {
@@ -887,6 +909,7 @@ pdf_document_class_init (PdfDocumentClass *klass)
 
 	ev_document_class->save = pdf_document_save;
 	ev_document_class->load = pdf_document_load;
+	ev_document_class->load_data = pdf_document_load_data;
 	ev_document_class->get_n_pages = pdf_document_get_n_pages;
 	ev_document_class->get_page = pdf_document_get_page;
 	ev_document_class->get_page_size = pdf_document_get_page_size;
diff --git a/libdocument/ev-document-factory.c b/libdocument/ev-document-factory.c
index 7aacb97..c71b6fb 100644
--- a/libdocument/ev-document-factory.c
+++ b/libdocument/ev-document-factory.c
@@ -125,6 +125,79 @@ get_document_from_uri (const char        *uri,
         return document;
 }
 
+/*
+ * get_document_from_data:
+ * @data: The contents of a file.
+ * @length: The number of bytes in @data.
+ * @compression: a location to store the document's compression type
+ * @error: a #GError location to store an error, or %NULL
+ *
+ * Creates a #EvDocument instance for the document contents.
+ * If a document could be created,
+ * @compression is filled in with the document's compression type.
+ * On error, %NULL is returned and @error filled in.
+ *
+ * Returns: a new #EvDocument instance, or %NULL on error with @error filled in
+ */
+static EvDocument *
+get_document_from_data (const guchar      *data,
+			gsize		   length,
+		        EvCompressionType *compression,
+		        GError           **error)
+{
+	EvDocument *document = NULL;
+	gchar	   *content_type = NULL;
+	gchar      *mime_type = NULL;
+
+	*compression = EV_COMPRESSION_NONE;
+
+	content_type = g_content_type_guess (NULL, data, length, NULL);
+	if (!content_type) {
+		g_set_error_literal (error,
+                                     EV_DOCUMENT_ERROR,
+                                     EV_DOCUMENT_ERROR_INVALID,
+                                     _("Unknown MIME Type"));
+
+		return NULL;
+	}
+
+	mime_type = g_content_type_get_mime_type (content_type);
+	if (!mime_type) {
+		g_set_error_literal (error,
+                                     EV_DOCUMENT_ERROR,
+                                     EV_DOCUMENT_ERROR_INVALID,
+                                     _("Unknown MIME Type"));
+		g_free (content_type);
+
+		return NULL;
+	}
+
+	document = ev_backends_manager_get_document (mime_type);
+	if (document == NULL) {
+		gchar *mime_desc = NULL;
+
+		mime_desc = g_content_type_get_description (content_type);
+
+		g_set_error (error,
+			     EV_DOCUMENT_ERROR,
+			     EV_DOCUMENT_ERROR_INVALID,
+			     _("File type %s (%s) is not supported"),
+			     mime_desc ? mime_desc : "-", mime_type);
+		g_free (mime_desc);
+		g_free (content_type);
+		g_free (mime_type);
+
+		return NULL;
+	}
+
+	*compression = get_compression_from_mime_type (mime_type);
+
+	g_free (content_type);
+	g_free (mime_type);
+
+        return document;
+}
+
 static void
 free_uncompressed_uri (gchar *uri_unc)
 {
@@ -237,6 +310,102 @@ ev_document_factory_get_document (const char *uri, GError **error)
 	return document;
 }
 
+static EvDocument *
+ev_document_factory_load_data (const guchar *data, gsize length, GError **error)
+{
+	EvDocument *document;
+	int result;
+	guchar* data_unc = NULL;
+	gsize data_unc_length = 0;
+	EvCompressionType compression;
+	GError *err = NULL;
+
+	document = get_document_from_data (data, length, &compression, &err);
+	g_assert (document != NULL || err != NULL);
+
+	if (document == NULL) {
+		/* Set an error for the caller. */
+		g_assert (err != NULL);
+		g_propagate_error (error, err);
+
+		return NULL;
+	}
+
+	/* TODO: Implement uncompress for data,
+	 * returning data, not a URI.
+	 * This currently uses command-line utilities on files.
+	 * data_unc = ev_file_uncompress (data, length, compression, &data_unc_length, &err);
+	 */
+	if (data_unc) {
+		g_object_set_data_full (G_OBJECT (document),
+					"data-uncompressed",
+					data_unc,
+					g_free);
+		g_object_set_data_full (G_OBJECT (document),
+					"data-length-uncompressed",
+					GSIZE_TO_POINTER(data_unc_length),
+					(GDestroyNotify) NULL);
+	} else if (err != NULL) {
+		/* Error uncompressing file */
+		g_object_unref (document);
+		g_propagate_error (error, err);
+		return NULL;
+	}
+
+	result = ev_document_load_from_data (document,
+					     data_unc ? data_unc : data,
+					     data_unc ? data_unc_length : length,
+					     &err);
+	if (result)
+		return document;
+
+	if (err) {
+		if (g_error_matches (err, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_ENCRYPTED)) {
+			g_propagate_error (error, err);
+			return document;
+		}
+	} else {
+		/* FIXME: this really should not happen; the backend should
+		 * always return a meaningful error.
+		 */
+		g_set_error_literal (&err,
+				     EV_DOCUMENT_ERROR,
+				     EV_DOCUMENT_ERROR_INVALID,
+				     _("Unknown MIME Type"));
+		g_propagate_error (error, err);
+	}
+
+	g_object_unref (document);
+
+	return NULL;
+}
+
+/**
+ * ev_document_factory_get_document_from_data:
+ * @data: The contents of a file.
+ * @length: The number of bytes in @data.
+ * @error: a #GError location to store an error, or %NULL
+ *
+ * Creates an #EvDocument for the document contents; or, if no backend handling
+ * the document's type is found, or an error occurred on opening the document,
+ * returns %NULL and fills in @error.
+ * If the document is encrypted, it is returned but also @error is set to
+ * %EV_DOCUMENT_ERROR_ENCRYPTED.
+ *
+ * Returns: (transfer full): a new #EvDocument, or %NULL.
+ */
+EvDocument *
+ev_document_factory_get_document_from_data (const guchar *data, gsize length, GError **error)
+{
+	g_return_val_if_fail (data != NULL, NULL);
+	g_return_val_if_fail (length != 0, NULL);
+
+	/* Note that, unlike ev_document_factory_get_document() we can't use
+	 * both slow and fast mime-type detection, so this is simpler.
+	 */
+	return ev_document_factory_load_data (data, length, error);
+}
+
 static void
 file_filter_add_mime_types (EvTypeInfo *info, GtkFileFilter *filter)
 {
diff --git a/libdocument/ev-document-factory.h b/libdocument/ev-document-factory.h
index 501eb2f..7c3a4e6 100644
--- a/libdocument/ev-document-factory.h
+++ b/libdocument/ev-document-factory.h
@@ -32,6 +32,7 @@
 G_BEGIN_DECLS
 
 EvDocument* ev_document_factory_get_document (const char *uri, GError **error);
+EvDocument* ev_document_factory_get_document_from_data (const guchar *data, gsize length, GError **error);
 void 	    ev_document_factory_add_filters  (GtkWidget *chooser, EvDocument *document);
 
 G_END_DECLS
diff --git a/libdocument/ev-document.c b/libdocument/ev-document.c
index a75862c..5f38374 100644
--- a/libdocument/ev-document.c
+++ b/libdocument/ev-document.c
@@ -208,6 +208,76 @@ ev_document_fc_mutex_trylock (void)
 	return g_mutex_trylock (p_ev_fc_mutex);
 }
 
+static void
+ev_document_setup_cache (EvDocument *document)
+{
+	gint i;
+	EvDocumentPrivate *priv = document->priv;
+
+	priv->n_pages = _ev_document_get_n_pages (document);
+
+	for (i = 0; i < priv->n_pages; i++) {
+		EvPage     *page = ev_document_get_page (document, i);
+		gdouble     page_width = 0;
+		gdouble     page_height = 0;
+		EvPageSize *page_size;
+		gchar      *page_label;
+
+		_ev_document_get_page_size (document, page, &page_width, &page_height);
+
+		if (i == 0) {
+			priv->uniform_width = page_width;
+			priv->uniform_height = page_height;
+			priv->max_width = priv->uniform_width;
+			priv->max_height = priv->uniform_height;
+			priv->min_width = priv->uniform_width;
+			priv->min_height = priv->uniform_height;
+		} else if (priv->uniform &&
+			   (priv->uniform_width != page_width ||
+			    priv->uniform_height != page_height)) {
+			/* It's a different page size.  Backfill the array. */
+			int j;
+
+			priv->page_sizes = g_new0 (EvPageSize, priv->n_pages);
+
+			for (j = 0; j < i; j++) {
+				page_size = &(priv->page_sizes[j]);
+				page_size->width = priv->uniform_width;
+				page_size->height = priv->uniform_height;
+			}
+			priv->uniform = FALSE;
+		}
+		if (!priv->uniform) {
+			page_size = &(priv->page_sizes[i]);
+
+			page_size->width = page_width;
+			page_size->height = page_height;
+
+			if (page_width > priv->max_width)
+				priv->max_width = page_width;
+			if (page_width < priv->min_width)
+				priv->min_width = page_width;
+
+			if (page_height > priv->max_height)
+				priv->max_height = page_height;
+			if (page_height < priv->min_height)
+				priv->min_height = page_height;
+		}
+
+		page_label = _ev_document_get_page_label (document, page);
+		if (page_label) {
+			if (!priv->page_labels)
+				priv->page_labels = g_new0 (gchar *, priv->n_pages);
+
+			priv->page_labels[i] = page_label;
+			priv->max_label = MAX (priv->max_label,
+					       g_utf8_strlen (page_label, 256));
+		}
+
+		g_object_unref (page);
+	}
+}
+
 /**
  * ev_document_load:
  * @document: a #EvDocument
@@ -249,81 +319,18 @@ ev_document_load (EvDocument  *document,
 					     "Internal error in backend");
 		}
 	} else {
-		gint i;
-		EvDocumentPrivate *priv = document->priv;
-
 		/* Cache some info about the document to avoid
 		 * going to the backends since it requires locks
 		 */
-		priv->uri = g_strdup (uri);
-		priv->n_pages = _ev_document_get_n_pages (document);
-
-		for (i = 0; i < priv->n_pages; i++) {
-			EvPage     *page = ev_document_get_page (document, i);
-			gdouble     page_width = 0;
-			gdouble     page_height = 0;
-			EvPageSize *page_size;
-			gchar      *page_label;
-
-			_ev_document_get_page_size (document, page, &page_width, &page_height);
-
-			if (i == 0) {
-				priv->uniform_width = page_width;
-				priv->uniform_height = page_height;
-				priv->max_width = priv->uniform_width;
-				priv->max_height = priv->uniform_height;
-				priv->min_width = priv->uniform_width;
-				priv->min_height = priv->uniform_height;
-			} else if (priv->uniform &&
-				   (priv->uniform_width != page_width ||
-				    priv->uniform_height != page_height)) {
-				/* It's a different page size.  Backfill the array. */
-				int j;
-
-				priv->page_sizes = g_new0 (EvPageSize, priv->n_pages);
-
-				for (j = 0; j < i; j++) {
-					page_size = &(priv->page_sizes[j]);
-					page_size->width = priv->uniform_width;
-					page_size->height = priv->uniform_height;
-				}
-				priv->uniform = FALSE;
-			}
-			if (!priv->uniform) {
-				page_size = &(priv->page_sizes[i]);
-
-				page_size->width = page_width;
-				page_size->height = page_height;
-
-				if (page_width > priv->max_width)
-					priv->max_width = page_width;
-				if (page_width < priv->min_width)
-					priv->min_width = page_width;
-
-				if (page_height > priv->max_height)
-					priv->max_height = page_height;
-				if (page_height < priv->min_height)
-					priv->min_height = page_height;
-			}
-
-			page_label = _ev_document_get_page_label (document, page);
-			if (page_label) {
-				if (!priv->page_labels)
-					priv->page_labels = g_new0 (gchar *, priv->n_pages);
-
-				priv->page_labels[i] = page_label;
-				priv->max_label = MAX (priv->max_label,
-						       g_utf8_strlen (page_label, 256));
-			}
-
-			g_object_unref (page);
-		}
+		EvDocumentPrivate *priv = document->priv;
 
+		priv->uri = g_strdup (uri);
+		ev_document_setup_cache (document);
+		/* TODO: Support synctex for data as well as URIs? */
 		priv->info = _ev_document_get_info (document);
 		if (_ev_document_support_synctex (document)) {
-			gchar *filename;
+			gchar *filename = g_filename_from_uri (uri, NULL, NULL);
 
-			filename = g_filename_from_uri (uri, NULL, NULL);
 			if (filename != NULL) {
 				priv->synctex_scanner =
 					synctex_scanner_new_with_output_file (filename, NULL, 1);
@@ -336,6 +343,64 @@ ev_document_load (EvDocument  *document,
 }
 
 /**
+ * ev_document_load_from_data:
+ * @document: a #EvDocument
+ * @data: the document's contents
+ * @length: the number of bytes in @data
+ * @error: a #GError location to store an error, or %NULL
+ *
+ * Loads @document from @data.
+ *
+ * On failure, %FALSE is returned and @error is filled in.
+ * If the document is encrypted, %EV_DOCUMENT_ERROR_ENCRYPTED is returned.
+ * If the backend cannot load the specific document, %EV_DOCUMENT_ERROR_INVALID
+ * is returned. Other errors are possible too, depending on the backend
+ * used to load the document and the data, e.g. #GIOError, #GFileError, and
+ * #GConvertError.
+ *
+ * Returns: %TRUE on success, or %FALSE on failure.
+ */
+gboolean
+ev_document_load_from_data (EvDocument   *document,
+			    const guchar *data,
+			    gsize         length,
+			    GError      **error)
+{
+	EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
+	gboolean retval;
+	GError *err = NULL;
+
+	if (klass->load_data == NULL) {
+		g_set_error_literal (error,
+				     EV_DOCUMENT_ERROR,
+				     EV_DOCUMENT_ERROR_UNSUPPORTED,
+				     "Backend does not support loading documents from data.");
+		return FALSE;
+	}
+	retval = klass->load_data (document, data, length, &err);
+	if (!retval) {
+		if (err) {
+			g_propagate_error (error, err);
+		} else {
+			g_warning ("%s::EvDocument::load_data returned FALSE but did not fill in @error; fix the backend!\n",
+				   G_OBJECT_TYPE_NAME (document));
+			/* So upper layers don't crash */
+			g_set_error_literal (error,
+					     EV_DOCUMENT_ERROR,
+					     EV_DOCUMENT_ERROR_INVALID,
+					     "Internal error in backend");
+		}
+	} else {
+		/* Cache some info about the document to avoid
+		 * going to the backends since it requires locks
+		 */
+	        ev_document_setup_cache (document);
+	}
+
+	return retval;
+}
+
+/**
  * ev_document_save:
  * @document:
  * @uri: the target URI
diff --git a/libdocument/ev-document.h b/libdocument/ev-document.h
index e1e01c0..bb95b28 100644
--- a/libdocument/ev-document.h
+++ b/libdocument/ev-document.h
@@ -56,7 +56,8 @@ typedef struct _EvDocumentPrivate EvDocumentPrivate;
 typedef enum
 {
         EV_DOCUMENT_ERROR_INVALID,
-        EV_DOCUMENT_ERROR_ENCRYPTED
+        EV_DOCUMENT_ERROR_ENCRYPTED,
+	EV_DOCUMENT_ERROR_UNSUPPORTED
 } EvDocumentError;
 
 typedef struct {
@@ -90,6 +91,10 @@ struct _EvDocumentClass
         gboolean          (* load)            (EvDocument      *document,
                                                const char      *uri,
                                                GError         **error);
+        gboolean          (* load_data)       (EvDocument      *document,
+                                               const guchar    *data,
+                                               gsize            length,
+                                               GError         **error);
         gboolean          (* save)            (EvDocument      *document,
                                                const char      *uri,
                                                GError         **error);
@@ -133,6 +138,10 @@ gboolean         ev_document_get_backend_info     (EvDocument      *document,
 gboolean         ev_document_load                 (EvDocument      *document,
 						   const char      *uri,
 						   GError         **error);
+gboolean         ev_document_load_from_data       (EvDocument      *document,
+						   const guchar    *data,
+						   gsize            length,
+						   GError         **error);
 gboolean         ev_document_save                 (EvDocument      *document,
 						   const char      *uri,
 						   GError         **error);
diff --git a/libview/ev-jobs.c b/libview/ev-jobs.c
index d7abaab..8839a36 100644
--- a/libview/ev-jobs.c
+++ b/libview/ev-jobs.c
@@ -972,8 +972,6 @@ ev_job_load_run (EvJob *job)
 	   because, e.g., a password is required - if so, just reload rather than
 	   creating a new instance */
 	if (job->document) {
-		const gchar *uncompressed_uri;
-
 		if (job_load->password) {
 			ev_document_security_set_password (EV_DOCUMENT_SECURITY (job->document),
 							   job_load->password);
@@ -983,14 +981,30 @@ ev_job_load_run (EvJob *job)
 		job->finished = FALSE;
 		g_clear_error (&job->error);
 
-		uncompressed_uri = g_object_get_data (G_OBJECT (job->document),
-						      "uri-uncompressed");
-		ev_document_load (job->document,
-				  uncompressed_uri ? uncompressed_uri : job_load->uri,
-				  &error);
-	} else {
+		if (job_load->uri) {
+			const gchar *uncompressed_uri = g_object_get_data (G_OBJECT (job->document),
+									   "uri-uncompressed");
+			ev_document_load (job->document,
+					  uncompressed_uri ? uncompressed_uri : job_load->uri,
+					  &error);
+		} else {
+			const guchar *uncompressed_data = g_object_get_data (G_OBJECT (job->document),
+									    "data-uncompressed");
+			gsize uncompressed_data_length = GPOINTER_TO_SIZE (g_object_get_data (G_OBJECT (job->document),
+											      "data-length-uncompressed"));
+
+			ev_document_load_from_data (job->document,
+						    uncompressed_data ? uncompressed_data : job_load->data,
+						    uncompressed_data ? uncompressed_data_length : job_load->data_length,
+						    &error);
+		}
+	} else if (job_load->uri) {
 		job->document = ev_document_factory_get_document (job_load->uri,
 								  &error);
+	} else {
+		job->document = ev_document_factory_get_document_from_data(job_load->data,
+									   job_load->data_length,
+									   &error);
 	}
 
 	ev_document_fc_mutex_unlock ();
@@ -1028,6 +1042,20 @@ ev_job_load_new (const gchar *uri)
 	return EV_JOB (job);
 }
 
+EvJob *
+ev_job_load_new_with_data (const guchar *data, gsize length)
+{
+	EvJobLoad *job;
+
+	ev_debug_message (DEBUG_JOBS, "data");
+
+	job = g_object_new (EV_TYPE_JOB_LOAD, NULL);
+	job->data = data;
+	job->data_length = length;
+
+	return EV_JOB (job);
+}
+
 void
 ev_job_load_set_uri (EvJobLoad *job, const gchar *uri)
 {
diff --git a/libview/ev-jobs.h b/libview/ev-jobs.h
index 7596d76..4fa5b0e 100644
--- a/libview/ev-jobs.h
+++ b/libview/ev-jobs.h
@@ -315,6 +315,8 @@ struct _EvJobLoad
 
 	gchar *uri;
 	gchar *password;
+	const guchar *data;
+	gsize data_length;
 };
 
 struct _EvJobLoadClass
@@ -457,6 +459,8 @@ EvJob 	       *ev_job_fonts_new 	  (EvDocument      *document);
 /* EvJobLoad */
 GType 		ev_job_load_get_type 	  (void) G_GNUC_CONST;
 EvJob 	       *ev_job_load_new 	  (const gchar 	   *uri);
+EvJob	       *ev_job_load_new_with_data (const guchar	   *data,
+					   gsize	    length);
 void            ev_job_load_set_uri       (EvJobLoad       *load,
 					   const gchar     *uri);
 void            ev_job_load_set_password  (EvJobLoad       *job,



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