[evince] Add ev_job_load_new_with_data() to load files from data in memory.
- From: Carlos Garcia Campos <carlosgc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evince] Add ev_job_load_new_with_data() to load files from data in memory.
- Date: Wed, 2 May 2012 12:04:23 +0000 (UTC)
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]