]> www.fi.muni.cz Git - evince.git/blob - backend/tiff/tiff-document.c
7f10c9ab7496b6edfdbd0e9244eacce26bdbea34
[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 #endif
264         if (rowstride / 4 != width)
265                 /* overflow, or cairo was changed in an unsupported way */
266                 return NULL;                
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                 guint32 *pixel = (guint32*)p;
296                 guint8 r = TIFFGetR(*pixel);
297                 guint8 g = TIFFGetG(*pixel);
298                 guint8 b = TIFFGetB(*pixel);
299                 guint8 a = TIFFGetA(*pixel);
300
301                 *pixel = (a << 24) | (r << 16) | (g << 8) | b;
302
303                 p += 4;
304         }
305
306         rotated_surface = ev_document_misc_surface_rotate_and_scale (surface,
307                                                                      (width * rc->scale) + 0.5,
308                                                                      (height * rc->scale * (x_res / y_res)) + 0.5,
309                                                                      rc->rotation);
310         cairo_surface_destroy (surface);
311         
312         return rotated_surface;
313 }
314
315 static GdkPixbuf *
316 tiff_document_render_pixbuf (EvDocument      *document,
317                              EvRenderContext *rc)
318 {
319         TiffDocument *tiff_document = TIFF_DOCUMENT (document);
320         int width, height;
321         float x_res, y_res;
322         gint rowstride, bytes;
323         guchar *pixels = NULL;
324         GdkPixbuf *pixbuf;
325         GdkPixbuf *scaled_pixbuf;
326         GdkPixbuf *rotated_pixbuf;
327         
328         push_handlers ();
329         if (TIFFSetDirectory (tiff_document->tiff, rc->page->index) != 1) {
330                 pop_handlers ();
331                 return NULL;
332         }
333
334         if (!TIFFGetField (tiff_document->tiff, TIFFTAG_IMAGEWIDTH, &width)) {
335                 pop_handlers ();
336                 return NULL;
337         }
338
339         if (! TIFFGetField (tiff_document->tiff, TIFFTAG_IMAGELENGTH, &height)) {
340                 pop_handlers ();
341                 return NULL;
342         }
343
344         tiff_document_get_resolution (tiff_document, &x_res, &y_res);
345         
346         pop_handlers ();
347   
348         /* Sanity check the doc */
349         if (width <= 0 || height <= 0)
350                 return NULL;                
351
352         rowstride = width * 4;
353         if (rowstride / 4 != width)
354                 /* overflow */
355                 return NULL;                
356         
357         bytes = height * rowstride;
358         if (bytes / rowstride != height)
359                 /* overflow */
360                 return NULL;                
361         
362         pixels = g_try_malloc (bytes);
363         if (!pixels)
364                 return NULL;
365         
366         pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8, 
367                                            width, height, rowstride,
368                                            (GdkPixbufDestroyNotify) g_free, NULL);
369         TIFFReadRGBAImageOriented (tiff_document->tiff,
370                                    width, height,
371                                    (uint32 *)pixels,
372                                    ORIENTATION_TOPLEFT, 1);
373         pop_handlers ();
374
375         scaled_pixbuf = gdk_pixbuf_scale_simple (pixbuf,
376                                                  width * rc->scale,
377                                                  height * rc->scale * (x_res / y_res),
378                                                  GDK_INTERP_BILINEAR);
379         g_object_unref (pixbuf);
380         
381         rotated_pixbuf = gdk_pixbuf_rotate_simple (scaled_pixbuf, 360 - rc->rotation);
382         g_object_unref (scaled_pixbuf);
383         
384         return rotated_pixbuf;
385 }
386
387 static void
388 tiff_document_finalize (GObject *object)
389 {
390         TiffDocument *tiff_document = TIFF_DOCUMENT (object);
391
392         if (tiff_document->tiff)
393                 TIFFClose (tiff_document->tiff);
394         if (tiff_document->uri)
395                 g_free (tiff_document->uri);
396
397         G_OBJECT_CLASS (tiff_document_parent_class)->finalize (object);
398 }
399
400 static void
401 tiff_document_class_init (TiffDocumentClass *klass)
402 {
403         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
404
405         gobject_class->finalize = tiff_document_finalize;
406 }
407
408 static gchar *
409 tiff_document_get_page_label (EvDocument *document,
410                               EvPage     *page)
411 {
412         TiffDocument *tiff_document = TIFF_DOCUMENT (document);
413         static gchar *label;
414         
415         if (TIFFGetField (tiff_document->tiff, TIFFTAG_PAGENAME, &label) &&
416             g_utf8_validate (label, -1, NULL)) {
417                 return g_strdup (label);
418         }
419         
420         return NULL;
421 }
422
423 static EvDocumentInfo *
424 tiff_document_get_info (EvDocument *document)
425 {
426         EvDocumentInfo *info;
427
428         info = g_new0 (EvDocumentInfo, 1);
429         info->fields_mask = 0;
430
431         return info;
432 }
433
434 static void
435 tiff_document_document_iface_init (EvDocumentIface *iface)
436 {
437         iface->load = tiff_document_load;
438         iface->save = tiff_document_save;
439         iface->get_n_pages = tiff_document_get_n_pages;
440         iface->get_page_size = tiff_document_get_page_size;
441         iface->render = tiff_document_render;
442         iface->get_page_label = tiff_document_get_page_label;
443         iface->get_info = tiff_document_get_info;
444 }
445
446 static GdkPixbuf *
447 tiff_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
448                                         EvRenderContext      *rc, 
449                                         gboolean              border)
450 {
451         GdkPixbuf *pixbuf;
452
453         pixbuf = tiff_document_render_pixbuf (EV_DOCUMENT (document), rc);
454         
455         if (border) {
456                 GdkPixbuf *tmp_pixbuf = pixbuf;
457                 
458                 pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf);
459                 g_object_unref (tmp_pixbuf);
460         }
461         
462         return pixbuf;
463 }
464
465 static void
466 tiff_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
467                                          EvRenderContext      *rc, 
468                                          gint                 *width,
469                                          gint                 *height)
470 {
471         gdouble page_width, page_height;
472
473         tiff_document_get_page_size (EV_DOCUMENT (document),
474                                      rc->page,
475                                      &page_width, &page_height);
476
477         if (rc->rotation == 90 || rc->rotation == 270) {
478                 *width = (gint) (page_height * rc->scale);
479                 *height = (gint) (page_width * rc->scale);
480         } else {
481                 *width = (gint) (page_width * rc->scale);
482                 *height = (gint) (page_height * rc->scale);
483         }
484 }
485
486 static void
487 tiff_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
488 {
489         iface->get_thumbnail = tiff_document_thumbnails_get_thumbnail;
490         iface->get_dimensions = tiff_document_thumbnails_get_dimensions;
491 }
492
493 /* postscript exporter implementation */
494 static void
495 tiff_document_file_exporter_begin (EvFileExporter        *exporter,
496                                    EvFileExporterContext *fc)
497 {
498         TiffDocument *document = TIFF_DOCUMENT (exporter);
499
500         document->ps_export_ctx = tiff2ps_context_new(fc->filename);
501 }
502
503 static void
504 tiff_document_file_exporter_do_page (EvFileExporter *exporter, EvRenderContext *rc)
505 {
506         TiffDocument *document = TIFF_DOCUMENT (exporter);
507
508         if (document->ps_export_ctx == NULL)
509                 return;
510         if (TIFFSetDirectory (document->tiff, rc->page->index) != 1)
511                 return;
512         tiff2ps_process_page (document->ps_export_ctx, document->tiff,
513                               0, 0, 0, 0, 0);
514 }
515
516 static void
517 tiff_document_file_exporter_end (EvFileExporter *exporter)
518 {
519         TiffDocument *document = TIFF_DOCUMENT (exporter);
520
521         if (document->ps_export_ctx == NULL)
522                 return;
523         tiff2ps_context_finalize(document->ps_export_ctx);
524 }
525
526 static EvFileExporterCapabilities
527 tiff_document_file_exporter_get_capabilities (EvFileExporter *exporter)
528 {
529         return  EV_FILE_EXPORTER_CAN_PAGE_SET |
530                 EV_FILE_EXPORTER_CAN_COPIES |
531                 EV_FILE_EXPORTER_CAN_COLLATE |
532                 EV_FILE_EXPORTER_CAN_REVERSE |
533                 EV_FILE_EXPORTER_CAN_GENERATE_PS;
534 }
535
536 static void
537 tiff_document_document_file_exporter_iface_init (EvFileExporterIface *iface)
538 {
539         iface->begin = tiff_document_file_exporter_begin;
540         iface->do_page = tiff_document_file_exporter_do_page;
541         iface->end = tiff_document_file_exporter_end;
542         iface->get_capabilities = tiff_document_file_exporter_get_capabilities;
543 }
544
545 static void
546 tiff_document_init (TiffDocument *tiff_document)
547 {
548         tiff_document->n_pages = -1;
549 }