]> www.fi.muni.cz Git - evince.git/blob - backend/tiff/tiff-document.c
Make sure to always set @error when returning FALSE.
[evince.git] / backend / tiff / tiff-document.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
2 /*
3  * Copyright (C) 2005, Jonathan Blandford <jrb@gnome.org>
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 /* FIXME: Should probably buffer calls to libtiff with TIFFSetWarningHandler
21  */
22
23 #include "config.h"
24
25 #include <config.h>
26 #include <stdio.h>
27 #include <glib.h>
28 #include <glib/gi18n-lib.h>
29
30 #include "tiffio.h"
31 #include "tiff2ps.h"
32 #include "tiff-document.h"
33 #include "ev-document-misc.h"
34 #include "ev-document-thumbnails.h"
35 #include "ev-file-exporter.h"
36 #include "ev-file-helpers.h"
37
38 struct _TiffDocumentClass
39 {
40   GObjectClass parent_class;
41 };
42
43 struct _TiffDocument
44 {
45   GObject parent_instance;
46
47   TIFF *tiff;
48   gint n_pages;
49   TIFF2PSContext *ps_export_ctx;
50   
51   gchar *uri;
52 };
53
54 typedef struct _TiffDocumentClass TiffDocumentClass;
55
56 static void tiff_document_document_iface_init (EvDocumentIface *iface);
57 static void tiff_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface);
58 static void tiff_document_document_file_exporter_iface_init (EvFileExporterIface *iface);
59
60 EV_BACKEND_REGISTER_WITH_CODE (TiffDocument, tiff_document,
61                          {
62                            EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
63                                                            tiff_document_document_thumbnails_iface_init);
64                            EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER,
65                                                            tiff_document_document_file_exporter_iface_init);
66                          });
67
68 static TIFFErrorHandler orig_error_handler = NULL;
69 static TIFFErrorHandler orig_warning_handler = NULL;
70
71 static void
72 push_handlers (void)
73 {
74         orig_error_handler = TIFFSetErrorHandler (NULL);
75         orig_warning_handler = TIFFSetWarningHandler (NULL);
76 }
77
78 static void
79 pop_handlers (void)
80 {
81         TIFFSetErrorHandler (orig_error_handler);
82         TIFFSetWarningHandler (orig_warning_handler);
83 }
84
85 static gboolean
86 tiff_document_load (EvDocument  *document,
87                     const char  *uri,
88                     GError     **error)
89 {
90         TiffDocument *tiff_document = TIFF_DOCUMENT (document);
91         gchar *filename;
92         TIFF *tiff;
93         
94         filename = g_filename_from_uri (uri, NULL, error);
95         if (!filename)
96                 return FALSE;
97         
98         push_handlers ();
99         tiff = TIFFOpen (filename, "r");
100         if (tiff) {
101                 guint32 w, h;
102                 
103                 /* FIXME: unused data? why bother here */
104                 TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &w);
105                 TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &h);
106         }
107         
108         if (!tiff) {
109                 pop_handlers ();
110
111                 g_set_error_literal (error,
112                                      EV_DOCUMENT_ERROR,
113                                      EV_DOCUMENT_ERROR_INVALID,
114                                      _("Invalid document"));
115
116                 g_free (filename);
117                 return FALSE;
118         }
119         
120         tiff_document->tiff = tiff;
121         g_free (tiff_document->uri);
122         g_free (filename);
123         tiff_document->uri = g_strdup (uri);
124         
125         pop_handlers ();
126         return TRUE;
127 }
128
129 static gboolean
130 tiff_document_save (EvDocument  *document,
131                     const char  *uri,
132                     GError     **error)
133 {               
134         TiffDocument *tiff_document = TIFF_DOCUMENT (document);
135
136         return ev_xfer_uri_simple (tiff_document->uri, uri, error); 
137 }
138
139 static int
140 tiff_document_get_n_pages (EvDocument  *document)
141 {
142         TiffDocument *tiff_document = TIFF_DOCUMENT (document);
143         
144         g_return_val_if_fail (TIFF_IS_DOCUMENT (document), 0);
145         g_return_val_if_fail (tiff_document->tiff != NULL, 0);
146         
147         if (tiff_document->n_pages == -1) {
148                 push_handlers ();
149                 tiff_document->n_pages = 0;
150                 
151                 do {
152                         tiff_document->n_pages ++;
153                 }
154                 while (TIFFReadDirectory (tiff_document->tiff));
155                 pop_handlers ();
156         }
157
158         return tiff_document->n_pages;
159 }
160
161 static void
162 tiff_document_get_resolution (TiffDocument *tiff_document,
163                               gfloat       *x_res,
164                               gfloat       *y_res)
165 {
166         gfloat x = 72.0, y = 72.0;
167         gushort unit;
168         
169         if (TIFFGetField (tiff_document->tiff, TIFFTAG_XRESOLUTION, &x) &&
170             TIFFGetField (tiff_document->tiff, TIFFTAG_YRESOLUTION, &y)) {
171                 if (TIFFGetFieldDefaulted (tiff_document->tiff, TIFFTAG_RESOLUTIONUNIT, &unit)) {
172                         if (unit == RESUNIT_CENTIMETER) {
173                                 x *= 2.54;
174                                 y *= 2.54;
175                         }
176                 }
177         }
178
179         *x_res = x;
180         *y_res = y;
181 }
182
183 static void
184 tiff_document_get_page_size (EvDocument *document,
185                              EvPage     *page,
186                              double     *width,
187                              double     *height)
188 {
189         guint32 w, h;
190         gfloat x_res, y_res;
191         TiffDocument *tiff_document = TIFF_DOCUMENT (document);
192         
193         g_return_if_fail (TIFF_IS_DOCUMENT (document));
194         g_return_if_fail (tiff_document->tiff != NULL);
195         
196         push_handlers ();
197         if (TIFFSetDirectory (tiff_document->tiff, page->index) != 1) {
198                 pop_handlers ();
199                 return;
200         }
201         
202         TIFFGetField (tiff_document->tiff, TIFFTAG_IMAGEWIDTH, &w);
203         TIFFGetField (tiff_document->tiff, TIFFTAG_IMAGELENGTH, &h);
204         tiff_document_get_resolution (tiff_document, &x_res, &y_res);
205         h = h * (x_res / y_res);
206         
207         *width = w;
208         *height = h;
209         
210         pop_handlers ();
211 }
212
213 static cairo_surface_t *
214 tiff_document_render (EvDocument      *document,
215                       EvRenderContext *rc)
216 {
217         TiffDocument *tiff_document = TIFF_DOCUMENT (document);
218         int width, height;
219         float x_res, y_res;
220         gint rowstride, bytes;
221         guchar *pixels = NULL;
222         guchar *p;
223         int orientation;
224         cairo_surface_t *surface;
225         cairo_surface_t *rotated_surface;
226         static const cairo_user_data_key_t key;
227         
228         g_return_val_if_fail (TIFF_IS_DOCUMENT (document), NULL);
229         g_return_val_if_fail (tiff_document->tiff != NULL, NULL);
230   
231         push_handlers ();
232         if (TIFFSetDirectory (tiff_document->tiff, rc->page->index) != 1) {
233                 pop_handlers ();
234                 return NULL;
235         }
236
237         if (!TIFFGetField (tiff_document->tiff, TIFFTAG_IMAGEWIDTH, &width)) {
238                 pop_handlers ();
239                 return NULL;
240         }
241
242         if (! TIFFGetField (tiff_document->tiff, TIFFTAG_IMAGELENGTH, &height)) {
243                 pop_handlers ();
244                 return NULL;
245         }
246
247         if (! TIFFGetField (tiff_document->tiff, TIFFTAG_ORIENTATION, &orientation)) {
248                 orientation = ORIENTATION_TOPLEFT;
249         }
250
251         tiff_document_get_resolution (tiff_document, &x_res, &y_res);
252         
253         pop_handlers ();
254   
255         /* Sanity check the doc */
256         if (width <= 0 || height <= 0)
257                 return NULL;                
258
259 #ifdef HAVE_CAIRO_FORMAT_STRIDE_FOR_WIDTH
260         rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, width);
261 #else
262         rowstride = width * 4;
263         if (rowstride / 4 != width)
264                 /* overflow */
265                 return NULL;                
266 #endif
267         
268         bytes = height * rowstride;
269         if (bytes / rowstride != height)
270                 /* overflow */
271                 return NULL;                
272         
273         pixels = g_try_malloc (bytes);
274         if (!pixels)
275                 return NULL;
276         
277         surface = cairo_image_surface_create_for_data (pixels,
278                                                        CAIRO_FORMAT_RGB24,
279                                                        width, height,
280                                                        rowstride);
281         cairo_surface_set_user_data (surface, &key,
282                                      pixels, (cairo_destroy_func_t)g_free);
283
284         TIFFReadRGBAImageOriented (tiff_document->tiff,
285                                    width, height,
286                                    (uint32 *)pixels,
287                                    orientation, 1);
288         pop_handlers ();
289
290         /* Convert the format returned by libtiff to
291         * what cairo expects
292         */
293         p = pixels;
294         while (p < pixels + bytes) {
295                 uint32 pixel = *(uint32 *)p;
296                 int r = TIFFGetR(pixel);
297                 int g = TIFFGetG(pixel);
298                 int b = TIFFGetB(pixel);
299                 int a = TIFFGetA(pixel);
300                 
301                 *p++ = b;
302                 *p++ = g;
303                 *p++ = r;
304                 *p++ = a;
305         }
306
307         rotated_surface = ev_document_misc_surface_rotate_and_scale (surface,
308                                                                      (width * rc->scale) + 0.5,
309                                                                      (height * rc->scale * (x_res / y_res)) + 0.5,
310                                                                      rc->rotation);
311         cairo_surface_destroy (surface);
312         
313         return rotated_surface;
314 }
315
316 static GdkPixbuf *
317 tiff_document_render_pixbuf (EvDocument      *document,
318                              EvRenderContext *rc)
319 {
320         TiffDocument *tiff_document = TIFF_DOCUMENT (document);
321         int width, height;
322         float x_res, y_res;
323         gint rowstride, bytes;
324         guchar *pixels = NULL;
325         GdkPixbuf *pixbuf;
326         GdkPixbuf *scaled_pixbuf;
327         GdkPixbuf *rotated_pixbuf;
328         
329         push_handlers ();
330         if (TIFFSetDirectory (tiff_document->tiff, rc->page->index) != 1) {
331                 pop_handlers ();
332                 return NULL;
333         }
334
335         if (!TIFFGetField (tiff_document->tiff, TIFFTAG_IMAGEWIDTH, &width)) {
336                 pop_handlers ();
337                 return NULL;
338         }
339
340         if (! TIFFGetField (tiff_document->tiff, TIFFTAG_IMAGELENGTH, &height)) {
341                 pop_handlers ();
342                 return NULL;
343         }
344
345         tiff_document_get_resolution (tiff_document, &x_res, &y_res);
346         
347         pop_handlers ();
348   
349         /* Sanity check the doc */
350         if (width <= 0 || height <= 0)
351                 return NULL;                
352
353         rowstride = width * 4;
354         if (rowstride / 4 != width)
355                 /* overflow */
356                 return NULL;                
357         
358         bytes = height * rowstride;
359         if (bytes / rowstride != height)
360                 /* overflow */
361                 return NULL;                
362         
363         pixels = g_try_malloc (bytes);
364         if (!pixels)
365                 return NULL;
366         
367         pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8, 
368                                            width, height, rowstride,
369                                            (GdkPixbufDestroyNotify) g_free, NULL);
370         TIFFReadRGBAImageOriented (tiff_document->tiff,
371                                    width, height,
372                                    (uint32 *)pixels,
373                                    ORIENTATION_TOPLEFT, 1);
374         pop_handlers ();
375
376         scaled_pixbuf = gdk_pixbuf_scale_simple (pixbuf,
377                                                  width * rc->scale,
378                                                  height * rc->scale * (x_res / y_res),
379                                                  GDK_INTERP_BILINEAR);
380         g_object_unref (pixbuf);
381         
382         rotated_pixbuf = gdk_pixbuf_rotate_simple (scaled_pixbuf, 360 - rc->rotation);
383         g_object_unref (scaled_pixbuf);
384         
385         return rotated_pixbuf;
386 }
387
388 static void
389 tiff_document_finalize (GObject *object)
390 {
391         TiffDocument *tiff_document = TIFF_DOCUMENT (object);
392
393         if (tiff_document->tiff)
394                 TIFFClose (tiff_document->tiff);
395         if (tiff_document->uri)
396                 g_free (tiff_document->uri);
397
398         G_OBJECT_CLASS (tiff_document_parent_class)->finalize (object);
399 }
400
401 static void
402 tiff_document_class_init (TiffDocumentClass *klass)
403 {
404         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
405
406         gobject_class->finalize = tiff_document_finalize;
407 }
408
409 static gchar *
410 tiff_document_get_page_label (EvDocument *document,
411                               EvPage     *page)
412 {
413         TiffDocument *tiff_document = TIFF_DOCUMENT (document);
414         static gchar *label;
415         
416         if (TIFFGetField (tiff_document->tiff, TIFFTAG_PAGENAME, &label) &&
417             g_utf8_validate (label, -1, NULL)) {
418                 return g_strdup (label);
419         }
420         
421         return NULL;
422 }
423
424 static EvDocumentInfo *
425 tiff_document_get_info (EvDocument *document)
426 {
427         EvDocumentInfo *info;
428
429         info = g_new0 (EvDocumentInfo, 1);
430         info->fields_mask = 0;
431
432         return info;
433 }
434
435 static void
436 tiff_document_document_iface_init (EvDocumentIface *iface)
437 {
438         iface->load = tiff_document_load;
439         iface->save = tiff_document_save;
440         iface->get_n_pages = tiff_document_get_n_pages;
441         iface->get_page_size = tiff_document_get_page_size;
442         iface->render = tiff_document_render;
443         iface->get_page_label = tiff_document_get_page_label;
444         iface->get_info = tiff_document_get_info;
445 }
446
447 static GdkPixbuf *
448 tiff_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
449                                         EvRenderContext      *rc, 
450                                         gboolean              border)
451 {
452         GdkPixbuf *pixbuf;
453
454         pixbuf = tiff_document_render_pixbuf (EV_DOCUMENT (document), rc);
455         
456         if (border) {
457                 GdkPixbuf *tmp_pixbuf = pixbuf;
458                 
459                 pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf);
460                 g_object_unref (tmp_pixbuf);
461         }
462         
463         return pixbuf;
464 }
465
466 static void
467 tiff_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
468                                          EvRenderContext      *rc, 
469                                          gint                 *width,
470                                          gint                 *height)
471 {
472         gdouble page_width, page_height;
473
474         tiff_document_get_page_size (EV_DOCUMENT (document),
475                                      rc->page,
476                                      &page_width, &page_height);
477
478         if (rc->rotation == 90 || rc->rotation == 270) {
479                 *width = (gint) (page_height * rc->scale);
480                 *height = (gint) (page_width * rc->scale);
481         } else {
482                 *width = (gint) (page_width * rc->scale);
483                 *height = (gint) (page_height * rc->scale);
484         }
485 }
486
487 static void
488 tiff_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
489 {
490         iface->get_thumbnail = tiff_document_thumbnails_get_thumbnail;
491         iface->get_dimensions = tiff_document_thumbnails_get_dimensions;
492 }
493
494 /* postscript exporter implementation */
495 static void
496 tiff_document_file_exporter_begin (EvFileExporter        *exporter,
497                                    EvFileExporterContext *fc)
498 {
499         TiffDocument *document = TIFF_DOCUMENT (exporter);
500
501         document->ps_export_ctx = tiff2ps_context_new(fc->filename);
502 }
503
504 static void
505 tiff_document_file_exporter_do_page (EvFileExporter *exporter, EvRenderContext *rc)
506 {
507         TiffDocument *document = TIFF_DOCUMENT (exporter);
508
509         if (document->ps_export_ctx == NULL)
510                 return;
511         if (TIFFSetDirectory (document->tiff, rc->page->index) != 1)
512                 return;
513         tiff2ps_process_page (document->ps_export_ctx, document->tiff,
514                               0, 0, 0, 0, 0);
515 }
516
517 static void
518 tiff_document_file_exporter_end (EvFileExporter *exporter)
519 {
520         TiffDocument *document = TIFF_DOCUMENT (exporter);
521
522         if (document->ps_export_ctx == NULL)
523                 return;
524         tiff2ps_context_finalize(document->ps_export_ctx);
525 }
526
527 static EvFileExporterCapabilities
528 tiff_document_file_exporter_get_capabilities (EvFileExporter *exporter)
529 {
530         return  EV_FILE_EXPORTER_CAN_PAGE_SET |
531                 EV_FILE_EXPORTER_CAN_COPIES |
532                 EV_FILE_EXPORTER_CAN_COLLATE |
533                 EV_FILE_EXPORTER_CAN_REVERSE |
534                 EV_FILE_EXPORTER_CAN_GENERATE_PS;
535 }
536
537 static void
538 tiff_document_document_file_exporter_iface_init (EvFileExporterIface *iface)
539 {
540         iface->begin = tiff_document_file_exporter_begin;
541         iface->do_page = tiff_document_file_exporter_do_page;
542         iface->end = tiff_document_file_exporter_end;
543         iface->get_capabilities = tiff_document_file_exporter_get_capabilities;
544 }
545
546 static void
547 tiff_document_init (TiffDocument *tiff_document)
548 {
549         tiff_document->n_pages = -1;
550 }