]> www.fi.muni.cz Git - evince.git/blob - libdocument/ev-document-factory.c
Add functions to delete temporary files created by evince in a safe way.
[evince.git] / libdocument / ev-document-factory.c
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "ev-document-factory.h"
26
27 /* The various document type backends: */
28 #ifdef ENABLE_PDF
29 #include "ev-poppler.h"
30 #endif
31 #ifdef ENABLE_PS
32 #include "ps-document.h"
33 #endif
34 #ifdef ENABLE_TIFF
35 #include "tiff-document.h"
36 #endif
37 #ifdef ENABLE_DVI
38 #include "dvi-document.h"
39 #endif
40 #ifdef ENABLE_PIXBUF
41 #include "pixbuf-document.h"
42 #endif
43 #ifdef ENABLE_DJVU
44 #include "djvu-document.h"
45 #endif
46 #ifdef ENABLE_COMICS
47 #include "comics-document.h"
48 #endif
49 #ifdef ENABLE_IMPRESS
50 #include "impress-document.h"
51 #endif
52
53 #include <string.h>
54 #include <glib/gstdio.h>
55 #include <glib/gi18n.h>
56 #include <libgnomevfs/gnome-vfs-mime-utils.h>
57 #include <libgnomevfs/gnome-vfs-file-info.h>
58 #include <libgnomevfs/gnome-vfs-ops.h>
59 #include <gtk/gtkfilechooserdialog.h>
60
61 #include "ev-file-helpers.h"
62
63 typedef struct _EvDocumentType EvDocumentType;
64 struct _EvDocumentType {
65         const char *mime_type;
66         EvCompressionType compression;
67         EvBackend backend;
68         GType (*document_type_factory_callback)();
69 };
70
71 const EvDocumentType document_types[] = {
72 #ifdef ENABLE_PDF
73         /* PDF: */
74         {"application/pdf",            EV_COMPRESSION_NONE,  EV_BACKEND_PDF,  pdf_document_get_type},
75         {"application/x-bzpdf",        EV_COMPRESSION_BZIP2, EV_BACKEND_PDF,  pdf_document_get_type},
76         {"application/x-gzpdf",        EV_COMPRESSION_GZIP,  EV_BACKEND_PDF,  pdf_document_get_type},
77 #endif
78
79 #ifdef ENABLE_PS
80         /* Postscript: */
81         {"application/postscript",     EV_COMPRESSION_NONE,  EV_BACKEND_PS,   ps_document_get_type},
82         {"application/x-bzpostscript", EV_COMPRESSION_BZIP2, EV_BACKEND_PS,   ps_document_get_type},
83         {"application/x-gzpostscript", EV_COMPRESSION_GZIP,  EV_BACKEND_PS,   ps_document_get_type},
84         {"image/x-eps",                EV_COMPRESSION_NONE,  EV_BACKEND_PS,   ps_document_get_type},
85         {"image/x-bzeps",              EV_COMPRESSION_BZIP2, EV_BACKEND_PS,   ps_document_get_type},
86         {"image/x-gzeps",              EV_COMPRESSION_GZIP,  EV_BACKEND_PS,   ps_document_get_type},
87 #endif
88
89 #ifdef ENABLE_TIFF
90         /* Tiff: */
91         {"image/tiff",                 EV_COMPRESSION_NONE, EV_BACKEND_TIFF, tiff_document_get_type},
92 #endif
93
94 #ifdef ENABLE_DJVU
95         /* djvu: */
96         {"image/vnd.djvu",             EV_COMPRESSION_NONE, EV_BACKEND_DJVU, djvu_document_get_type},
97 #endif          
98
99 #ifdef ENABLE_DVI
100         /* dvi: */
101         {"application/x-dvi",          EV_COMPRESSION_NONE, EV_BACKEND_DVI,  dvi_document_get_type},
102 #endif
103
104 #ifdef ENABLE_COMICS
105         /* cbr/cbz: */
106         {"application/x-cbr",          EV_COMPRESSION_NONE, EV_BACKEND_COMICS,  comics_document_get_type},
107         {"application/x-cbz",          EV_COMPRESSION_NONE, EV_BACKEND_COMICS,  comics_document_get_type},
108 #endif
109
110 #ifdef ENABLE_IMPRESS
111         /* Impress slides: */
112         {"application/vnd.sun.xml.impress", EV_COMPRESSION_NONE, EV_BACKEND_IMPRESS, impress_document_get_type},
113         {"application/vnd.oasis.opendocument.presentation", EV_COMPRESSION_NONE, EV_BACKEND_IMPRESS, impress_document_get_type},
114 #endif
115
116 };
117
118 #ifdef ENABLE_PIXBUF
119
120 static GList*
121 gdk_pixbuf_mime_type_list ()
122 {
123         GSList *formats, *list;
124         GList *result;
125
126         formats = gdk_pixbuf_get_formats ();
127         result = NULL;
128
129         for (list = formats; list != NULL; list = list->next) {
130                 GdkPixbufFormat *format = list->data;
131                 int i;
132                 gchar **mime_types;
133
134                 if (gdk_pixbuf_format_is_disabled (format))
135                         continue;
136
137                 mime_types = gdk_pixbuf_format_get_mime_types (format);
138
139                 for (i = 0; mime_types[i] != NULL; i++) {
140                         result = g_list_append (result, mime_types[i]);
141                 }
142         }
143         g_slist_free (formats);
144
145         return result;
146 }
147
148 /* Would be nice to have this in gdk-pixbuf */
149 static gboolean
150 mime_type_supported_by_gdk_pixbuf (const gchar *mime_type)
151 {
152         GList *mime_types;
153         GList *list;
154         gboolean retval = FALSE;
155         
156         mime_types = gdk_pixbuf_mime_type_list ();
157         for (list = mime_types; list; list = list->next) {
158                 if (strcmp ((char *)list->data, mime_type) == 0) {
159                         retval = TRUE;
160                         break;
161                 }
162         }
163         
164         g_list_foreach (mime_types, (GFunc)g_free, NULL);
165         g_list_free (mime_types);
166
167         return retval;
168 }
169 #endif
170
171 static EvDocument *
172 ev_document_factory_get_from_mime (const gchar       *mime_type,
173                                    EvCompressionType *compression)
174 {
175         int i;
176         GType type = G_TYPE_INVALID;
177         EvDocument *document = NULL;
178
179         *compression = EV_COMPRESSION_NONE;
180         
181         for (i = 0; i < G_N_ELEMENTS (document_types); i++) {
182                 if (strcmp (mime_type, document_types[i].mime_type) == 0) {
183                         g_assert (document_types[i].document_type_factory_callback != NULL);
184                         type = document_types[i].document_type_factory_callback ();
185                         *compression = document_types[i].compression;
186                         break;
187                 }
188         }
189 #ifdef ENABLE_PIXBUF
190         if (type == G_TYPE_INVALID && mime_type_supported_by_gdk_pixbuf (mime_type)) {
191                 type = pixbuf_document_get_type ();
192         }
193 #endif
194
195         if (type != G_TYPE_INVALID) {
196                 document = g_object_new (type, NULL);
197         } 
198
199         return document;
200 }
201
202 EvBackend
203 ev_document_factory_get_backend (EvDocument *document)
204 {
205         int i;
206
207         for (i = 0; i < G_N_ELEMENTS (document_types); i++) {
208                 GType type = document_types[i].document_type_factory_callback ();
209                 if (type == G_TYPE_FROM_INSTANCE (document)) {
210                         return  document_types[i].backend;
211                 }
212         }
213
214 #ifdef ENABLE_PIXBUF
215         if (G_TYPE_FROM_INSTANCE (document) == pixbuf_document_get_type ())
216                 return EV_BACKEND_PIXBUF;
217 #endif
218         g_assert_not_reached ();
219         
220         return 0;
221 }
222
223 static GList *
224 ev_document_factory_get_mime_types (EvBackend backend)
225 {
226         GList *types = NULL;
227         int i;
228         
229 #ifdef ENABLE_PIXBUF
230         if (backend == EV_BACKEND_PIXBUF) {
231                 return gdk_pixbuf_mime_type_list ();
232         }
233 #endif
234         
235         for (i = 0; i < G_N_ELEMENTS (document_types); i++) {
236                 if (document_types[i].backend == backend) {
237                         types = g_list_append (types, g_strdup (document_types[i].mime_type));
238                 }
239         }
240
241         return types;
242 }
243
244 static GList *
245 ev_document_factory_get_all_mime_types (void)
246 {
247         GList *types = NULL;
248         int i;
249         
250         for (i = 0; i < G_N_ELEMENTS (document_types); i++) {
251                 types = g_list_append (types, g_strdup (document_types[i].mime_type));
252         }
253         
254 #ifdef ENABLE_PIXBUF
255         types = g_list_concat (types, gdk_pixbuf_mime_type_list ());
256 #endif
257
258         return types;
259 }
260
261 static EvDocument *
262 get_document_from_uri (const char        *uri,
263                        gboolean           slow,
264                        EvCompressionType *compression,
265                        GError           **error)
266 {
267         EvDocument *document = NULL;
268         GnomeVFSFileInfo *info;
269         GnomeVFSResult result;
270
271         *compression = EV_COMPRESSION_NONE;
272
273         info = gnome_vfs_file_info_new ();
274         result = gnome_vfs_get_file_info (uri, info,
275                                           GNOME_VFS_FILE_INFO_GET_MIME_TYPE |
276                                           GNOME_VFS_FILE_INFO_FOLLOW_LINKS | 
277                                           (slow ? GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE : 0));
278         if (result != GNOME_VFS_OK) {
279                 g_set_error (error,
280                              EV_DOCUMENT_ERROR,
281                              0,
282                              gnome_vfs_result_to_string (result));                      
283                 gnome_vfs_file_info_unref (info);
284                 return NULL;
285         } 
286         
287         if (info->mime_type == NULL) {
288                 g_set_error (error,
289                              EV_DOCUMENT_ERROR, 
290                              0,
291                              _("Unknown MIME Type"));
292                 gnome_vfs_file_info_unref (info);
293                 return NULL;
294         }
295
296         document = ev_document_factory_get_from_mime (info->mime_type, compression);
297                 
298         if (document == NULL) {
299                 g_set_error (error,
300                              EV_DOCUMENT_ERROR, 
301                              0,
302                              _("Unhandled MIME type: ā€œ%sā€"), info->mime_type);
303                 gnome_vfs_file_info_unref (info);
304                 return NULL;
305         }
306
307         gnome_vfs_file_info_unref (info);
308         
309         return document;
310 }
311
312 static void
313 free_uncompressed_uri (gchar *uri_unc)
314 {
315         if (!uri_unc)
316                 return;
317
318         ev_tmp_uri_unlink (uri_unc);
319         g_free (uri_unc);
320 }
321
322 EvDocument *
323 ev_document_factory_get_document (const char *uri, GError **error)
324 {
325         EvDocument *document;
326         int result;
327         EvCompressionType compression;
328         gchar *uri_unc = NULL;
329
330         document = get_document_from_uri (uri, FALSE, &compression, error);
331         if (*error == NULL) {
332                 uri_unc = ev_file_uncompress (uri, compression, error);
333                 if (uri_unc) {
334                         g_object_set_data_full (G_OBJECT (document),
335                                                 "uri-uncompressed",
336                                                 uri_unc,
337                                                 (GDestroyNotify) free_uncompressed_uri);
338                 }
339
340                 if (*error != NULL) {
341                         /* Error uncompressing file */
342                         if (document)
343                                 g_object_unref (document);
344                         return NULL;
345                 }
346
347                 result = ev_document_load (document, uri_unc ? uri_unc : uri, error);
348
349                 if (result == FALSE || *error) {
350                         if (*error &&
351                             (*error)->domain == EV_DOCUMENT_ERROR &&
352                             (*error)->code == EV_DOCUMENT_ERROR_ENCRYPTED)
353                                 return document;
354                 } else {
355                         return document;
356                 }
357         }
358         
359         /* Try again with slow mime detection */
360         if (document)
361                 g_object_unref (document);
362         document = NULL;
363
364         if (*error)
365                 g_error_free (*error);
366         *error = NULL;
367
368         uri_unc = NULL;
369
370         document = get_document_from_uri (uri, TRUE, &compression, error);
371
372         if (*error != NULL) {
373                 return NULL;
374         }
375
376         uri_unc = ev_file_uncompress (uri, compression, error);
377         if (uri_unc) {
378                 g_object_set_data_full (G_OBJECT (document),
379                                         "uri-uncompressed",
380                                         uri_unc,
381                                         (GDestroyNotify) free_uncompressed_uri);
382         }
383
384         if (*error != NULL) {
385                 /* Error uncompressing file */
386                 if (document)
387                         g_object_unref (document);
388                 return NULL;
389         }
390         
391         result = ev_document_load (document, uri_unc ? uri_unc : uri, error);
392
393         if (result == FALSE) {
394                 if (*error == NULL) {
395                         g_set_error (error,
396                                      EV_DOCUMENT_ERROR,
397                                      0,
398                                      _("Unknown MIME Type"));
399                 } else if ((*error)->domain == EV_DOCUMENT_ERROR &&
400                            (*error)->code == EV_DOCUMENT_ERROR_ENCRYPTED) {
401                         return document;
402                 }
403
404                 if (document)
405                         g_object_unref (document);
406                 document = NULL;
407         }
408         
409         return document;
410 }
411
412 static void
413 file_filter_add_mime_list_and_free (GtkFileFilter *filter, GList *mime_types)
414 {
415         GList *l;
416
417         for (l = mime_types; l != NULL; l = l->next) {
418                 gtk_file_filter_add_mime_type (filter, l->data);
419         }
420
421         g_list_foreach (mime_types, (GFunc)g_free, NULL);
422         g_list_free (mime_types);
423 }
424
425 void 
426 ev_document_factory_add_filters (GtkWidget *chooser, EvDocument *document)
427 {
428         EvBackend backend = 0;
429         GList *mime_types;
430         GtkFileFilter *filter;
431         GtkFileFilter *default_filter;
432         GtkFileFilter *document_filter;
433
434         if (document != NULL) {
435                 backend = ev_document_factory_get_backend (document);
436         }
437
438         default_filter = document_filter = filter = gtk_file_filter_new ();
439         gtk_file_filter_set_name (filter, _("All Documents"));
440         mime_types = ev_document_factory_get_all_mime_types ();
441         file_filter_add_mime_list_and_free (filter, mime_types);
442         gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
443
444 #ifdef ENABLE_PS
445         if (document == NULL || backend == EV_BACKEND_PS) {
446                 default_filter = filter = gtk_file_filter_new ();
447                 gtk_file_filter_set_name (filter, _("PostScript Documents"));
448                 mime_types = ev_document_factory_get_mime_types (EV_BACKEND_PS);
449                 file_filter_add_mime_list_and_free (filter, mime_types);
450                 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
451         }
452 #endif
453
454 #ifdef ENABLE_PDF
455         if (document == NULL || backend == EV_BACKEND_PDF) {
456                 default_filter = filter = gtk_file_filter_new ();
457                 gtk_file_filter_set_name (filter, _("PDF Documents"));
458                 mime_types = ev_document_factory_get_mime_types (EV_BACKEND_PDF);
459                 file_filter_add_mime_list_and_free (filter, mime_types);
460                 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
461         }
462 #endif
463
464 #ifdef ENABLE_PIXBUF
465         if (document == NULL || backend == EV_BACKEND_PIXBUF) {
466                 default_filter = filter = gtk_file_filter_new ();
467                 gtk_file_filter_set_name (filter, _("Images"));
468                 mime_types = ev_document_factory_get_mime_types (EV_BACKEND_PIXBUF);
469                 file_filter_add_mime_list_and_free (filter, mime_types);
470                 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
471         }
472 #endif
473
474 #ifdef ENABLE_DVI
475         if (document == NULL || backend == EV_BACKEND_DVI) {
476                 default_filter = filter = gtk_file_filter_new ();
477                 gtk_file_filter_set_name (filter, _("DVI Documents"));
478                 mime_types = ev_document_factory_get_mime_types (EV_BACKEND_DVI);
479                 file_filter_add_mime_list_and_free (filter, mime_types);
480                 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
481         }
482 #endif
483
484 #ifdef ENABLE_DJVU
485         if (document == NULL || backend == EV_BACKEND_DJVU) {
486                 default_filter = filter = gtk_file_filter_new ();
487                 gtk_file_filter_set_name (filter, _("Djvu Documents"));
488                 mime_types = ev_document_factory_get_mime_types (EV_BACKEND_DJVU);
489                 file_filter_add_mime_list_and_free (filter, mime_types);
490                 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
491         }
492 #endif  
493
494 #ifdef ENABLE_COMICS
495         if (document == NULL || backend == EV_BACKEND_COMICS) {
496                 default_filter = filter = gtk_file_filter_new ();
497                 gtk_file_filter_set_name (filter, _("Comic Books"));
498                 mime_types = ev_document_factory_get_mime_types (EV_BACKEND_COMICS);
499                 file_filter_add_mime_list_and_free (filter, mime_types);
500                 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
501         }
502 #endif  
503
504 #ifdef ENABLE_IMPRESS
505         if (document == NULL || backend == EV_BACKEND_IMPRESS) {
506                 default_filter = filter = gtk_file_filter_new ();
507                 gtk_file_filter_set_name (filter, _("Impress Slides"));
508                 mime_types = ev_document_factory_get_mime_types (EV_BACKEND_IMPRESS);
509                 file_filter_add_mime_list_and_free (filter, mime_types);
510                 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
511         }
512 #endif  
513
514         filter = gtk_file_filter_new ();
515         gtk_file_filter_set_name (filter, _("All Files"));
516         gtk_file_filter_add_pattern (filter, "*");
517         gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
518
519         gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (chooser),
520                                      document == NULL ? document_filter : default_filter);
521 }