Evince
Evince is a document viewer capable of displaying multiple and single page document formats like PDF and Postscript.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ev-document-factory.c
Go to the documentation of this file.
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
2 /*
3  * Copyright (C) 2005, Red Hat, Inc.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program 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
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <string.h>
26 
27 #include <gio/gio.h>
28 #include <glib/gstdio.h>
29 #include <glib/gi18n-lib.h>
30 #include <gtk/gtk.h>
31 
32 #include "ev-backend-info.h"
33 #include "ev-document-factory.h"
34 #include "ev-file-helpers.h"
35 #include "ev-module.h"
36 
37 #include "ev-backends-manager.h"
38 
39 /* Backends manager */
40 
41 #define BACKEND_DATA_KEY "ev-backend-info"
42 
43 static GList *ev_backends_list = NULL;
44 static GHashTable *ev_module_hash = NULL;
45 static gchar *ev_backends_dir = NULL;
46 
47 static EvDocument* ev_document_factory_new_document_for_mime_type (const char *mime_type,
48  GError **error);
49 
50 static EvBackendInfo *
51 get_backend_info_for_mime_type (const gchar *mime_type)
52 {
53  GList *l;
54 
55  for (l = ev_backends_list; l; l = l->next) {
56  EvBackendInfo *info = (EvBackendInfo *) l->data;
57  char **mime_types = info->mime_types;
58  guint i;
59 
60  for (i = 0; mime_types[i] != NULL; ++i) {
61  if (g_ascii_strcasecmp (mime_type, mime_types[i]) == 0)
62  return info;
63  }
64  }
65 
66  return NULL;
67 }
68 
69 static EvBackendInfo *
71 {
72  EvBackendInfo *info;
73 
74  info = g_object_get_data (G_OBJECT (document), BACKEND_DATA_KEY);
75 
76  g_warn_if_fail (info != NULL);
77  return info;
78 }
79 
80 static EvDocument *
82  GError **error)
83 {
84  EvDocument *document;
85  EvBackendInfo *info;
86  GTypeModule *module = NULL;
87 
88  g_return_val_if_fail (mime_type != NULL, NULL);
89 
90  info = get_backend_info_for_mime_type (mime_type);
91  if (info == NULL) {
92  char *content_type, *mime_desc = NULL;
93 
94  content_type = g_content_type_from_mime_type (mime_type);
95  if (content_type)
96  mime_desc = g_content_type_get_description (content_type);
97 
98  g_set_error (error,
101  _("File type %s (%s) is not supported"),
102  mime_desc ? mime_desc : "(unknown)", mime_type);
103  g_free (mime_desc);
104  g_free (content_type);
105 
106  return NULL;
107  }
108 
109  if (ev_module_hash != NULL) {
110  module = g_hash_table_lookup (ev_module_hash, info->module_name);
111  }
112  if (module == NULL) {
113  gchar *path;
114 
115  path = g_module_build_path (ev_backends_dir, info->module_name);
116  module = G_TYPE_MODULE (_ev_module_new (path, info->resident));
117  g_free (path);
118 
119  if (ev_module_hash == NULL) {
120  ev_module_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
121  g_free,
122  NULL /* leaked on purpose */);
123  }
124  g_hash_table_insert (ev_module_hash, g_strdup (info->module_name), module);
125  }
126 
127  if (!g_type_module_use (module)) {
128  const char *err;
129 
130  err = g_module_error ();
131  g_set_error (error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_INVALID,
132  "Failed to load backend for '%s': %s",
133  mime_type, err ? err : "unknown error");
134  return NULL;
135  }
136 
137  document = EV_DOCUMENT (_ev_module_new_object (EV_MODULE (module)));
138  g_type_module_unuse (module);
139 
140  g_object_set_data_full (G_OBJECT (document), BACKEND_DATA_KEY,
141  _ev_backend_info_ref (info),
142  (GDestroyNotify) _ev_backend_info_unref);
143 
144  return document;
145 }
146 
147 static EvCompressionType
148 get_compression_from_mime_type (const gchar *mime_type)
149 {
150  gchar type[3];
151  gchar *p;
152 
153  if (!(p = g_strrstr (mime_type, "/")))
154  return EV_COMPRESSION_NONE;
155 
156  if (sscanf (++p, "x-%2s%*s", type) == 1) {
157  if (g_ascii_strcasecmp (type, "gz") == 0)
158  return EV_COMPRESSION_GZIP;
159  else if (g_ascii_strcasecmp (type, "bz") == 0)
160  return EV_COMPRESSION_BZIP2;
161  else if (g_ascii_strcasecmp (type, "xz") == 0)
162  return EV_COMPRESSION_LZMA;
163  }
164 
165  return EV_COMPRESSION_NONE;
166 }
167 
168 
169 /*
170  * new_document_for_uri:
171  * @uri: the document URI
172  * @fast: whether to use fast MIME type detection
173  * @compression: a location to store the document's compression type
174  * @error: a #GError location to store an error, or %NULL
175  *
176  * Creates a #EvDocument instance for the document at @uri, using either
177  * fast or slow MIME type detection. If a document could be created,
178  * @compression is filled in with the document's compression type.
179  * On error, %NULL is returned and @error filled in.
180  *
181  * Returns: a new #EvDocument instance, or %NULL on error with @error filled in
182  */
183 static EvDocument *
184 new_document_for_uri (const char *uri,
185  gboolean fast,
186  EvCompressionType *compression,
187  GError **error)
188 {
189  EvDocument *document = NULL;
190  gchar *mime_type = NULL;
191 
192  *compression = EV_COMPRESSION_NONE;
193 
194  mime_type = ev_file_get_mime_type (uri, fast, error);
195  if (mime_type == NULL)
196  return NULL;
197 
198  document = ev_document_factory_new_document_for_mime_type (mime_type, error);
199  if (document == NULL)
200  return NULL;
201 
202  *compression = get_compression_from_mime_type (mime_type);
203 
204  g_free (mime_type);
205 
206  return document;
207 }
208 
209 static void
210 free_uncompressed_uri (gchar *uri_unc)
211 {
212  if (!uri_unc)
213  return;
214 
215  ev_tmp_uri_unlink (uri_unc);
216  g_free (uri_unc);
217 }
218 
219 /*
220  * _ev_document_factory_init:
221  *
222  * Initializes the evince document factory.
223  *
224  * Returns: %TRUE if there were any backends found; %FALSE otherwise
225  */
226 gboolean
228 {
229  if (ev_backends_list)
230  return TRUE;
231 
232 #ifdef G_OS_WIN32
233 {
234  gchar *dir;
235 
236  dir = g_win32_get_package_installation_directory_of_module (NULL);
237  ev_backends_dir = g_build_filename (dir, "lib", "evince",
238  EV_BACKENDSBINARYVERSION,
239  "backends", NULL);
240  g_free (dir);
241 }
242 #else
243  ev_backends_dir = g_strdup (EV_BACKENDSDIR);
244 #endif
245 
247 
248  return ev_backends_list != NULL;
249 }
250 
251 /*
252  * _ev_document_factory_shutdown:
253  *
254  * Shuts the evince document factory down.
255  */
256 void
258 {
259  g_list_foreach (ev_backends_list, (GFunc) _ev_backend_info_unref, NULL);
260  g_list_free (ev_backends_list);
261  ev_backends_list = NULL;
262 
263  if (ev_module_hash != NULL) {
264  g_hash_table_unref (ev_module_hash);
265  ev_module_hash = NULL;
266  }
267 
268  g_free (ev_backends_dir);
269  ev_backends_dir = NULL;
270 }
271 
286 EvDocument *
288  EvDocumentLoadFlags flags,
289  GError **error)
290 {
291  EvDocument *document;
292  int result;
293  EvCompressionType compression;
294  gchar *uri_unc = NULL;
295  GError *err = NULL;
296 
297  g_return_val_if_fail (uri != NULL, NULL);
298 
299  document = new_document_for_uri (uri, TRUE, &compression, &err);
300  g_assert (document != NULL || err != NULL);
301 
302  if (document != NULL) {
303  uri_unc = ev_file_uncompress (uri, compression, &err);
304  if (uri_unc) {
305  g_object_set_data_full (G_OBJECT (document),
306  "uri-uncompressed",
307  uri_unc,
308  (GDestroyNotify) free_uncompressed_uri);
309  } else if (err != NULL) {
310  /* Error uncompressing file */
311  g_object_unref (document);
312  g_propagate_error (error, err);
313  return NULL;
314  }
315 
316  result = ev_document_load_full (document, uri_unc ? uri_unc : uri, flags, &err);
317 
318  if (result == FALSE || err) {
319  if (err &&
320  g_error_matches (err, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_ENCRYPTED)) {
321  g_propagate_error (error, err);
322  return document;
323  }
324  /* else fall through to slow mime code section below */
325  } else {
326  return document;
327  }
328 
329  g_object_unref (document);
330  document = NULL;
331  }
332 
333  /* Try again with slow mime detection */
334  g_clear_error (&err);
335  uri_unc = NULL;
336 
337  document = new_document_for_uri (uri, FALSE, &compression, &err);
338  if (document == NULL) {
339  g_assert (err != NULL);
340  g_propagate_error (error, err);
341  return NULL;
342  }
343 
344  uri_unc = ev_file_uncompress (uri, compression, &err);
345  if (uri_unc) {
346  g_object_set_data_full (G_OBJECT (document),
347  "uri-uncompressed",
348  uri_unc,
349  (GDestroyNotify) free_uncompressed_uri);
350  } else if (err != NULL) {
351  /* Error uncompressing file */
352  g_propagate_error (error, err);
353 
354  g_object_unref (document);
355  return NULL;
356  }
357 
358  result = ev_document_load_full (document, uri_unc ? uri_unc : uri,
360  if (result == FALSE) {
361  if (err == NULL) {
362  /* FIXME: this really should not happen; the backend should
363  * always return a meaningful error.
364  */
365  g_set_error_literal (&err,
368  _("Unknown MIME Type"));
369  } else if (g_error_matches (err, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_ENCRYPTED)) {
370  g_propagate_error (error, err);
371  return document;
372  }
373 
374  g_object_unref (document);
375  document = NULL;
376 
377  g_propagate_error (error, err);
378  }
379 
380  return document;
381 }
382 
396 EvDocument *
397 ev_document_factory_get_document (const char *uri, GError **error)
398 {
401  error);
402 }
403 
421 EvDocument*
423  EvDocumentLoadFlags flags,
424  GCancellable *cancellable,
425  GError **error)
426 {
427  EvDocument *document;
428  GFileInfo *file_info;
429  const char *content_type;
430  char *mime_type = NULL;
431 
432  g_return_val_if_fail (G_IS_FILE (file), NULL);
433  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
434 
435 
436  file_info = g_file_query_info (file,
437  G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
438  G_FILE_QUERY_INFO_NONE,
439  cancellable,
440  error);
441  if (file_info == NULL)
442  return NULL;
443 
444  content_type = g_file_info_get_content_type (file_info);
445  if (content_type == NULL) {
446  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
447  "Failed to query file mime type");
448  return NULL;
449  }
450 
451  mime_type = g_content_type_get_mime_type (content_type);
452  g_object_unref (file_info);
453 
454  document = ev_document_factory_new_document_for_mime_type (mime_type, error);
455  g_free (mime_type);
456  if (document == NULL)
457  return NULL;
458 
459  if (!ev_document_load_gfile (document, file, flags, cancellable, error)) {
460  g_object_unref (document);
461  return NULL;
462  }
463 
464  return document;
465 }
466 
489 EvDocument*
491  const char *mime_type,
492  EvDocumentLoadFlags flags,
493  GCancellable *cancellable,
494  GError **error)
495 {
496  EvDocument *document;
497  char *mime = NULL;
498 
499  g_return_val_if_fail (G_IS_INPUT_STREAM (stream), NULL);
500  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
501 
502  if (mime_type == NULL && G_IS_FILE_INPUT_STREAM (stream)) {
503  GFileInfo *file_info;
504  const char *content_type;
505 
506  file_info = g_file_input_stream_query_info (G_FILE_INPUT_STREAM (stream),
507  G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
508  cancellable,
509  error);
510  if (file_info != NULL) {
511  content_type = g_file_info_get_content_type (file_info);
512  if (content_type)
513  mime_type = mime = g_content_type_get_mime_type (content_type);
514  g_object_unref (file_info);
515  }
516  }
517 
518  if (mime_type == NULL) {
519  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
520  "Cannot query mime type from stream");
521  return NULL;
522  }
523 
524  document = ev_document_factory_new_document_for_mime_type (mime_type, error);
525  g_free (mime);
526 
527  if (document == NULL)
528  return NULL;
529 
530  if (!ev_document_load_stream (document, stream, flags, cancellable, error)) {
531  g_object_unref (document);
532  return NULL;
533  }
534 
535  return document;
536 }
537 
538 static void
539 file_filter_add_mime_types (EvBackendInfo *info, GtkFileFilter *filter)
540 {
541  char **mime_types;
542  guint i;
543 
544  mime_types = info->mime_types;
545  if (mime_types == NULL)
546  return;
547 
548  for (i = 0; mime_types[i] != NULL; ++i)
549  gtk_file_filter_add_mime_type (filter, mime_types[i]);
550 }
551 
566 void
567 ev_document_factory_add_filters (GtkWidget *chooser, EvDocument *document)
568 {
569  GtkFileFilter *filter;
570  GtkFileFilter *default_filter;
571  GtkFileFilter *document_filter;
572 
573  g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
574  g_return_if_fail (document == NULL || EV_IS_DOCUMENT (document));
575 
576  default_filter = document_filter = filter = gtk_file_filter_new ();
577  gtk_file_filter_set_name (filter, _("All Documents"));
578  g_list_foreach (ev_backends_list, (GFunc)file_filter_add_mime_types, filter);
579  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
580 
581  if (document) {
582  EvBackendInfo *info;
583 
584  info = get_backend_info_for_document (document);
585  g_assert (info != NULL);
586  default_filter = filter = gtk_file_filter_new ();
587  gtk_file_filter_set_name (filter, info->type_desc);
588  file_filter_add_mime_types (info, filter);
589  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
590  } else {
591  GList *l;
592 
593  for (l = ev_backends_list; l; l = l->next) {
594  EvBackendInfo *info = (EvBackendInfo *) l->data;
595 
596  default_filter = filter = gtk_file_filter_new ();
597  gtk_file_filter_set_name (filter, info->type_desc);
598  file_filter_add_mime_types (info, filter);
599  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
600  }
601  }
602 
603  filter = gtk_file_filter_new ();
604  gtk_file_filter_set_name (filter, _("All Files"));
605  gtk_file_filter_add_pattern (filter, "*");
606  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
607 
608  gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (chooser),
609  document == NULL ? document_filter : default_filter);
610 }
611 
612 /* Deprecated API/ABI compatibility wrappers */
613 
621 {
622  return ev_document_factory_new_document_for_mime_type (mime_type, NULL);
623 }
624 
625 const gchar *
627 {
628  EvBackendInfo *info = get_backend_info_for_document (document);
629  if (info == NULL)
630  return NULL;
631 
632  return info->module_name;
633 }
634 
636 {
637  return (EvTypeInfo *) get_backend_info_for_document (document);
638 }
639 
640 GList *
642 {
643  return g_list_copy (ev_backends_list);
644 }