]> www.fi.muni.cz Git - evince.git/blob - libdocument/ev-document-factory.c
Support for PDF, PS and EPS compressed files. Fixes bug #307087.
[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         gchar *filename;
316
317         if (!uri_unc)
318                 return;
319
320         filename = g_filename_from_uri (uri_unc, NULL, NULL);
321         if (!filename)
322                 return;
323
324         g_unlink (filename);
325         g_free (filename);
326         g_free (uri_unc);
327 }
328
329 EvDocument *
330 ev_document_factory_get_document (const char *uri, GError **error)
331 {
332         EvDocument *document;
333         int result;
334         EvCompressionType compression;
335         gchar *uri_unc = NULL;
336
337         document = get_document_from_uri (uri, FALSE, &compression, error);
338         if (*error == NULL) {
339                 uri_unc = ev_file_uncompress (uri, compression, error);
340                 if (uri_unc) {
341                         g_object_set_data_full (G_OBJECT (document),
342                                                 "uri-uncompressed",
343                                                 uri_unc,
344                                                 (GDestroyNotify) free_uncompressed_uri);
345                 }
346
347                 if (*error != NULL) {
348                         /* Error uncompressing file */
349                         if (document)
350                                 g_object_unref (document);
351                         return NULL;
352                 }
353
354                 result = ev_document_load (document, uri_unc ? uri_unc : uri, error);
355
356                 if (result == FALSE || *error) {
357                         if (*error &&
358                             (*error)->domain == EV_DOCUMENT_ERROR &&
359                             (*error)->code == EV_DOCUMENT_ERROR_ENCRYPTED)
360                                 return document;
361                 } else {
362                         return document;
363                 }
364         }
365         
366         /* Try again with slow mime detection */
367         if (document)
368                 g_object_unref (document);
369         document = NULL;
370
371         if (*error)
372                 g_error_free (*error);
373         *error = NULL;
374
375         uri_unc = NULL;
376
377         document = get_document_from_uri (uri, TRUE, &compression, error);
378
379         if (*error != NULL) {
380                 return NULL;
381         }
382
383         uri_unc = ev_file_uncompress (uri, compression, error);
384         if (uri_unc) {
385                 g_object_set_data_full (G_OBJECT (document),
386                                         "uri-uncompressed",
387                                         uri_unc,
388                                         (GDestroyNotify) free_uncompressed_uri);
389         }
390
391         if (*error != NULL) {
392                 /* Error uncompressing file */
393                 if (document)
394                         g_object_unref (document);
395                 return NULL;
396         }
397         
398         result = ev_document_load (document, uri_unc ? uri_unc : uri, error);
399
400         if (result == FALSE) {
401                 if (*error == NULL) {
402                         g_set_error (error,
403                                      EV_DOCUMENT_ERROR,
404                                      0,
405                                      _("Unknown MIME Type"));
406                 } else if ((*error)->domain == EV_DOCUMENT_ERROR &&
407                            (*error)->code == EV_DOCUMENT_ERROR_ENCRYPTED) {
408                         return document;
409                 }
410
411                 if (document)
412                         g_object_unref (document);
413                 document = NULL;
414         }
415         
416         return document;
417 }
418
419 static void
420 file_filter_add_mime_list_and_free (GtkFileFilter *filter, GList *mime_types)
421 {
422         GList *l;
423
424         for (l = mime_types; l != NULL; l = l->next) {
425                 gtk_file_filter_add_mime_type (filter, l->data);
426         }
427
428         g_list_foreach (mime_types, (GFunc)g_free, NULL);
429         g_list_free (mime_types);
430 }
431
432 void 
433 ev_document_factory_add_filters (GtkWidget *chooser, EvDocument *document)
434 {
435         EvBackend backend = 0;
436         GList *mime_types;
437         GtkFileFilter *filter;
438         GtkFileFilter *default_filter;
439         GtkFileFilter *document_filter;
440
441         if (document != NULL) {
442                 backend = ev_document_factory_get_backend (document);
443         }
444
445         default_filter = document_filter = filter = gtk_file_filter_new ();
446         gtk_file_filter_set_name (filter, _("All Documents"));
447         mime_types = ev_document_factory_get_all_mime_types ();
448         file_filter_add_mime_list_and_free (filter, mime_types);
449         gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
450
451 #ifdef ENABLE_PS
452         if (document == NULL || backend == EV_BACKEND_PS) {
453                 default_filter = filter = gtk_file_filter_new ();
454                 gtk_file_filter_set_name (filter, _("PostScript Documents"));
455                 mime_types = ev_document_factory_get_mime_types (EV_BACKEND_PS);
456                 file_filter_add_mime_list_and_free (filter, mime_types);
457                 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
458         }
459 #endif
460
461 #ifdef ENABLE_PDF
462         if (document == NULL || backend == EV_BACKEND_PDF) {
463                 default_filter = filter = gtk_file_filter_new ();
464                 gtk_file_filter_set_name (filter, _("PDF Documents"));
465                 mime_types = ev_document_factory_get_mime_types (EV_BACKEND_PDF);
466                 file_filter_add_mime_list_and_free (filter, mime_types);
467                 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
468         }
469 #endif
470
471 #ifdef ENABLE_PIXBUF
472         if (document == NULL || backend == EV_BACKEND_PIXBUF) {
473                 default_filter = filter = gtk_file_filter_new ();
474                 gtk_file_filter_set_name (filter, _("Images"));
475                 mime_types = ev_document_factory_get_mime_types (EV_BACKEND_PIXBUF);
476                 file_filter_add_mime_list_and_free (filter, mime_types);
477                 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
478         }
479 #endif
480
481 #ifdef ENABLE_DVI
482         if (document == NULL || backend == EV_BACKEND_DVI) {
483                 default_filter = filter = gtk_file_filter_new ();
484                 gtk_file_filter_set_name (filter, _("DVI Documents"));
485                 mime_types = ev_document_factory_get_mime_types (EV_BACKEND_DVI);
486                 file_filter_add_mime_list_and_free (filter, mime_types);
487                 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
488         }
489 #endif
490
491 #ifdef ENABLE_DJVU
492         if (document == NULL || backend == EV_BACKEND_DJVU) {
493                 default_filter = filter = gtk_file_filter_new ();
494                 gtk_file_filter_set_name (filter, _("Djvu Documents"));
495                 mime_types = ev_document_factory_get_mime_types (EV_BACKEND_DJVU);
496                 file_filter_add_mime_list_and_free (filter, mime_types);
497                 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
498         }
499 #endif  
500
501 #ifdef ENABLE_COMICS
502         if (document == NULL || backend == EV_BACKEND_COMICS) {
503                 default_filter = filter = gtk_file_filter_new ();
504                 gtk_file_filter_set_name (filter, _("Comic Books"));
505                 mime_types = ev_document_factory_get_mime_types (EV_BACKEND_COMICS);
506                 file_filter_add_mime_list_and_free (filter, mime_types);
507                 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
508         }
509 #endif  
510
511 #ifdef ENABLE_IMPRESS
512         if (document == NULL || backend == EV_BACKEND_IMPRESS) {
513                 default_filter = filter = gtk_file_filter_new ();
514                 gtk_file_filter_set_name (filter, _("Impress Slides"));
515                 mime_types = ev_document_factory_get_mime_types (EV_BACKEND_IMPRESS);
516                 file_filter_add_mime_list_and_free (filter, mime_types);
517                 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
518         }
519 #endif  
520
521         filter = gtk_file_filter_new ();
522         gtk_file_filter_set_name (filter, _("All Files"));
523         gtk_file_filter_add_pattern (filter, "*");
524         gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
525
526         gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (chooser),
527                                      document == NULL ? document_filter : default_filter);
528 }