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