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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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   EvDocumentClass parent_class;
41 };
42 
43 struct _TiffDocument
44 {
45   EvDocument 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_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface);
57 static void tiff_document_document_file_exporter_iface_init (EvFileExporterInterface *iface);
58 
59 EV_BACKEND_REGISTER_WITH_CODE (TiffDocument, tiff_document,
60 			 {
61 			   EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
62 							   tiff_document_document_thumbnails_iface_init);
63 			   EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER,
64 							   tiff_document_document_file_exporter_iface_init);
65 			 });
66 
67 static TIFFErrorHandler orig_error_handler = NULL;
68 static TIFFErrorHandler orig_warning_handler = NULL;
69 
70 static void
push_handlers(void)71 push_handlers (void)
72 {
73 	orig_error_handler = TIFFSetErrorHandler (NULL);
74 	orig_warning_handler = TIFFSetWarningHandler (NULL);
75 }
76 
77 static void
pop_handlers(void)78 pop_handlers (void)
79 {
80 	TIFFSetErrorHandler (orig_error_handler);
81 	TIFFSetWarningHandler (orig_warning_handler);
82 }
83 
84 static gboolean
tiff_document_load(EvDocument * document,const char * uri,GError ** error)85 tiff_document_load (EvDocument  *document,
86 		    const char  *uri,
87 		    GError     **error)
88 {
89 	TiffDocument *tiff_document = TIFF_DOCUMENT (document);
90 	gchar *filename;
91 	TIFF *tiff;
92 
93 	filename = g_filename_from_uri (uri, NULL, error);
94 	if (!filename)
95 		return FALSE;
96 
97 	push_handlers ();
98 
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
tiff_document_save(EvDocument * document,const char * uri,GError ** error)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
tiff_document_get_n_pages(EvDocument * document)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
tiff_document_get_resolution(TiffDocument * tiff_document,gfloat * x_res,gfloat * y_res)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
tiff_document_get_page_size(EvDocument * document,EvPage * page,double * width,double * height)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 *
tiff_document_render(EvDocument * document,EvRenderContext * rc)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 		g_warning("Failed to select page %d", rc->page->index);
235 		return NULL;
236 	}
237 
238 	if (!TIFFGetField (tiff_document->tiff, TIFFTAG_IMAGEWIDTH, &width)) {
239 		pop_handlers ();
240 		g_warning("Failed to read image width");
241 		return NULL;
242 	}
243 
244 	if (! TIFFGetField (tiff_document->tiff, TIFFTAG_IMAGELENGTH, &height)) {
245 		pop_handlers ();
246 		g_warning("Failed to read image height");
247 		return NULL;
248 	}
249 
250 	if (! TIFFGetField (tiff_document->tiff, TIFFTAG_ORIENTATION, &orientation)) {
251 		orientation = ORIENTATION_TOPLEFT;
252 	}
253 
254 	tiff_document_get_resolution (tiff_document, &x_res, &y_res);
255 
256 	pop_handlers ();
257 
258 	/* Sanity check the doc */
259 	if (width <= 0 || height <= 0) {
260 		g_warning("Invalid width or height.");
261 		return NULL;
262 	}
263 
264 	rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, width);
265 	if (rowstride / 4 != width) {
266 		g_warning("Overflow while rendering document.");
267 		/* overflow, or cairo was changed in an unsupported way */
268 		return NULL;
269 	}
270 
271 	bytes = height * rowstride;
272 	if (bytes / rowstride != height) {
273 		g_warning("Overflow while rendering document.");
274 		/* overflow */
275 		return NULL;
276 	}
277 
278 	pixels = g_try_malloc (bytes);
279 	if (!pixels) {
280 		g_warning("Failed to allocate memory for rendering.");
281 		return NULL;
282 	}
283 
284 	surface = cairo_image_surface_create_for_data (pixels,
285 						       CAIRO_FORMAT_RGB24,
286 						       width, height,
287 						       rowstride);
288 	cairo_surface_set_user_data (surface, &key,
289 				     pixels, (cairo_destroy_func_t)g_free);
290 
291 	TIFFReadRGBAImageOriented (tiff_document->tiff,
292 				   width, height,
293 				   (uint32 *)pixels,
294 				   orientation, 0);
295 	pop_handlers ();
296 
297 	/* Convert the format returned by libtiff to
298 	* what cairo expects
299 	*/
300 	p = pixels;
301 	while (p < pixels + bytes) {
302 		guint32 *pixel = (guint32*)p;
303 		guint8 r = TIFFGetR(*pixel);
304 		guint8 g = TIFFGetG(*pixel);
305 		guint8 b = TIFFGetB(*pixel);
306 		guint8 a = TIFFGetA(*pixel);
307 
308 		*pixel = (a << 24) | (r << 16) | (g << 8) | b;
309 
310 		p += 4;
311 	}
312 
313 	rotated_surface = ev_document_misc_surface_rotate_and_scale (surface,
314 								     (width * rc->scale) + 0.5,
315 								     (height * rc->scale * (x_res / y_res)) + 0.5,
316 								     rc->rotation);
317 	cairo_surface_destroy (surface);
318 
319 	return rotated_surface;
320 }
321 
322 static GdkPixbuf *
tiff_document_render_pixbuf(EvDocument * document,EvRenderContext * rc)323 tiff_document_render_pixbuf (EvDocument      *document,
324 			     EvRenderContext *rc)
325 {
326 	TiffDocument *tiff_document = TIFF_DOCUMENT (document);
327 	int width, height;
328 	float x_res, y_res;
329 	gint rowstride, bytes;
330 	guchar *pixels = NULL;
331 	GdkPixbuf *pixbuf;
332 	GdkPixbuf *scaled_pixbuf;
333 	GdkPixbuf *rotated_pixbuf;
334 
335 	push_handlers ();
336 	if (TIFFSetDirectory (tiff_document->tiff, rc->page->index) != 1) {
337 		pop_handlers ();
338 		return NULL;
339 	}
340 
341 	if (!TIFFGetField (tiff_document->tiff, TIFFTAG_IMAGEWIDTH, &width)) {
342 		pop_handlers ();
343 		return NULL;
344 	}
345 
346 	if (! TIFFGetField (tiff_document->tiff, TIFFTAG_IMAGELENGTH, &height)) {
347 		pop_handlers ();
348 		return NULL;
349 	}
350 
351 	tiff_document_get_resolution (tiff_document, &x_res, &y_res);
352 
353 	pop_handlers ();
354 
355 	/* Sanity check the doc */
356 	if (width <= 0 || height <= 0)
357 		return NULL;
358 
359 	rowstride = width * 4;
360 	if (rowstride / 4 != width)
361 		/* overflow */
362 		return NULL;
363 
364 	bytes = height * rowstride;
365 	if (bytes / rowstride != height)
366 		/* overflow */
367 		return NULL;
368 
369 	pixels = g_try_malloc (bytes);
370 	if (!pixels)
371 		return NULL;
372 
373 	pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8,
374 					   width, height, rowstride,
375 					   (GdkPixbufDestroyNotify) g_free, NULL);
376 	TIFFReadRGBAImageOriented (tiff_document->tiff,
377 				   width, height,
378 				   (uint32 *)pixels,
379 				   ORIENTATION_TOPLEFT, 0);
380 	pop_handlers ();
381 
382 	scaled_pixbuf = gdk_pixbuf_scale_simple (pixbuf,
383 						 width * rc->scale,
384 						 height * rc->scale * (x_res / y_res),
385 						 GDK_INTERP_BILINEAR);
386 	g_object_unref (pixbuf);
387 
388 	rotated_pixbuf = gdk_pixbuf_rotate_simple (scaled_pixbuf, 360 - rc->rotation);
389 	g_object_unref (scaled_pixbuf);
390 
391 	return rotated_pixbuf;
392 }
393 
394 static gchar *
tiff_document_get_page_label(EvDocument * document,EvPage * page)395 tiff_document_get_page_label (EvDocument *document,
396 			      EvPage     *page)
397 {
398 	TiffDocument *tiff_document = TIFF_DOCUMENT (document);
399 	static gchar *label;
400 
401 	if (TIFFGetField (tiff_document->tiff, TIFFTAG_PAGENAME, &label) &&
402 	    g_utf8_validate (label, -1, NULL)) {
403 		return g_strdup (label);
404 	}
405 
406 	return NULL;
407 }
408 
409 static void
tiff_document_finalize(GObject * object)410 tiff_document_finalize (GObject *object)
411 {
412 	TiffDocument *tiff_document = TIFF_DOCUMENT (object);
413 
414 	if (tiff_document->tiff)
415 		TIFFClose (tiff_document->tiff);
416 	if (tiff_document->uri)
417 		g_free (tiff_document->uri);
418 
419 	G_OBJECT_CLASS (tiff_document_parent_class)->finalize (object);
420 }
421 
422 static void
tiff_document_class_init(TiffDocumentClass * klass)423 tiff_document_class_init (TiffDocumentClass *klass)
424 {
425 	GObjectClass    *gobject_class = G_OBJECT_CLASS (klass);
426 	EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass);
427 
428 	gobject_class->finalize = tiff_document_finalize;
429 
430 	ev_document_class->load = tiff_document_load;
431 	ev_document_class->save = tiff_document_save;
432 	ev_document_class->get_n_pages = tiff_document_get_n_pages;
433 	ev_document_class->get_page_size = tiff_document_get_page_size;
434 	ev_document_class->render = tiff_document_render;
435 	ev_document_class->get_page_label = tiff_document_get_page_label;
436 }
437 
438 static GdkPixbuf *
tiff_document_thumbnails_get_thumbnail(EvDocumentThumbnails * document,EvRenderContext * rc,gboolean border)439 tiff_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
440 					EvRenderContext      *rc,
441 					gboolean              border)
442 {
443 	GdkPixbuf *pixbuf;
444 
445 	pixbuf = tiff_document_render_pixbuf (EV_DOCUMENT (document), rc);
446 
447 	if (border) {
448 		GdkPixbuf *tmp_pixbuf = pixbuf;
449 
450 		pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf);
451 		g_object_unref (tmp_pixbuf);
452 	}
453 
454 	return pixbuf;
455 }
456 
457 static void
tiff_document_thumbnails_get_dimensions(EvDocumentThumbnails * document,EvRenderContext * rc,gint * width,gint * height)458 tiff_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
459 					 EvRenderContext      *rc,
460 					 gint                 *width,
461 					 gint                 *height)
462 {
463 	gdouble page_width, page_height;
464 
465 	tiff_document_get_page_size (EV_DOCUMENT (document),
466 				     rc->page,
467 				     &page_width, &page_height);
468 
469 	if (rc->rotation == 90 || rc->rotation == 270) {
470 		*width = (gint) (page_height * rc->scale);
471 		*height = (gint) (page_width * rc->scale);
472 	} else {
473 		*width = (gint) (page_width * rc->scale);
474 		*height = (gint) (page_height * rc->scale);
475 	}
476 }
477 
478 static void
tiff_document_document_thumbnails_iface_init(EvDocumentThumbnailsInterface * iface)479 tiff_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface)
480 {
481 	iface->get_thumbnail = tiff_document_thumbnails_get_thumbnail;
482 	iface->get_dimensions = tiff_document_thumbnails_get_dimensions;
483 }
484 
485 /* postscript exporter implementation */
486 static void
tiff_document_file_exporter_begin(EvFileExporter * exporter,EvFileExporterContext * fc)487 tiff_document_file_exporter_begin (EvFileExporter        *exporter,
488 				   EvFileExporterContext *fc)
489 {
490 	TiffDocument *document = TIFF_DOCUMENT (exporter);
491 
492 	document->ps_export_ctx = tiff2ps_context_new(fc->filename);
493 }
494 
495 static void
tiff_document_file_exporter_do_page(EvFileExporter * exporter,EvRenderContext * rc)496 tiff_document_file_exporter_do_page (EvFileExporter *exporter, EvRenderContext *rc)
497 {
498 	TiffDocument *document = TIFF_DOCUMENT (exporter);
499 
500 	if (document->ps_export_ctx == NULL)
501 		return;
502 	if (TIFFSetDirectory (document->tiff, rc->page->index) != 1)
503 		return;
504 	tiff2ps_process_page (document->ps_export_ctx, document->tiff,
505 			      0, 0, 0, 0, 0);
506 }
507 
508 static void
tiff_document_file_exporter_end(EvFileExporter * exporter)509 tiff_document_file_exporter_end (EvFileExporter *exporter)
510 {
511 	TiffDocument *document = TIFF_DOCUMENT (exporter);
512 
513 	if (document->ps_export_ctx == NULL)
514 		return;
515 	tiff2ps_context_finalize(document->ps_export_ctx);
516 }
517 
518 static EvFileExporterCapabilities
tiff_document_file_exporter_get_capabilities(EvFileExporter * exporter)519 tiff_document_file_exporter_get_capabilities (EvFileExporter *exporter)
520 {
521 	return  EV_FILE_EXPORTER_CAN_PAGE_SET |
522 		EV_FILE_EXPORTER_CAN_COPIES |
523 		EV_FILE_EXPORTER_CAN_COLLATE |
524 		EV_FILE_EXPORTER_CAN_REVERSE |
525 		EV_FILE_EXPORTER_CAN_GENERATE_PS;
526 }
527 
528 static void
tiff_document_document_file_exporter_iface_init(EvFileExporterInterface * iface)529 tiff_document_document_file_exporter_iface_init (EvFileExporterInterface *iface)
530 {
531 	iface->begin = tiff_document_file_exporter_begin;
532 	iface->do_page = tiff_document_file_exporter_do_page;
533 	iface->end = tiff_document_file_exporter_end;
534 	iface->get_capabilities = tiff_document_file_exporter_get_capabilities;
535 }
536 
537 static void
tiff_document_init(TiffDocument * tiff_document)538 tiff_document_init (TiffDocument *tiff_document)
539 {
540 	tiff_document->n_pages = -1;
541 }
542