1 /* GXPSImages
2  *
3  * Copyright (C) 2010  Carlos Garcia Campos <carlosgc@gnome.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #include <config.h>
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdint.h>
26 
27 #ifdef HAVE_LIBPNG
28 #include <png.h>
29 #endif
30 
31 #ifdef HAVE_LIBJPEG
32 #include <jpeglib.h>
33 #include <setjmp.h>
34 #endif
35 
36 #ifdef HAVE_LIBTIFF
37 #include <tiffio.h>
38 #endif
39 
40 #include "gxps-images.h"
41 #include "gxps-error.h"
42 #include "gxps-debug.h"
43 
44 #define METERS_PER_INCH 0.0254
45 #define CENTIMETERS_PER_INCH 2.54
46 
47 /* PNG */
48 #ifdef HAVE_LIBPNG
49 
50 static const cairo_user_data_key_t image_data_cairo_key;
51 
52 static void
_read_png(png_structp png_ptr,png_bytep data,png_size_t len)53 _read_png (png_structp png_ptr,
54 	   png_bytep data,
55 	   png_size_t len)
56 {
57 	GInputStream *stream;
58 
59 	stream = png_get_io_ptr (png_ptr);
60 	g_input_stream_read (stream, data, len, NULL, NULL);
61 }
62 
63 static void
png_error_callback(png_structp png_ptr,png_const_charp error_msg)64 png_error_callback (png_structp png_ptr,
65 		    png_const_charp error_msg)
66 {
67 	char **msg;
68 
69 	msg = png_get_error_ptr (png_ptr);
70 	*msg = g_strdup (error_msg);
71 	longjmp (png_jmpbuf (png_ptr), 1);
72 }
73 
74 static void
png_warning_callback(png_structp png,png_const_charp error_msg)75 png_warning_callback (png_structp png,
76 		      png_const_charp error_msg)
77 {
78 }
79 
80 /* From cairo's cairo-png.c <http://cairographics.org> */
81 static inline int
multiply_alpha(int alpha,int color)82 multiply_alpha (int alpha, int color)
83 {
84 	int temp = (alpha * color) + 0x80;
85 
86 	return ((temp + (temp >> 8)) >> 8);
87 }
88 
89 /* Premultiplies data and converts RGBA bytes => native endian
90  * From cairo's cairo-png.c <http://cairographics.org> */
91 static void
premultiply_data(png_structp png,png_row_infop row_info,png_bytep data)92 premultiply_data (png_structp   png,
93                   png_row_infop row_info,
94                   png_bytep     data)
95 {
96 	unsigned int i;
97 
98 	for (i = 0; i < row_info->rowbytes; i += 4) {
99 		uint8_t *base  = &data[i];
100 		uint8_t  alpha = base[3];
101 		uint32_t p;
102 
103 		if (alpha == 0) {
104 			p = 0;
105 		} else {
106 			uint8_t red   = base[0];
107 			uint8_t green = base[1];
108 			uint8_t blue  = base[2];
109 
110 			if (alpha != 0xff) {
111 				red   = multiply_alpha (alpha, red);
112 				green = multiply_alpha (alpha, green);
113 				blue  = multiply_alpha (alpha, blue);
114 			}
115 			p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
116 		}
117 		memcpy (base, &p, sizeof (uint32_t));
118 	}
119 }
120 
121 /* Converts RGBx bytes to native endian xRGB
122  * From cairo's cairo-png.c <http://cairographics.org> */
123 static void
convert_bytes_to_data(png_structp png,png_row_infop row_info,png_bytep data)124 convert_bytes_to_data (png_structp png, png_row_infop row_info, png_bytep data)
125 {
126 	unsigned int i;
127 
128 	for (i = 0; i < row_info->rowbytes; i += 4) {
129 		uint8_t *base  = &data[i];
130 		uint8_t  red   = base[0];
131 		uint8_t  green = base[1];
132 		uint8_t  blue  = base[2];
133 		uint32_t pixel;
134 
135 		pixel = (0xff << 24) | (red << 16) | (green << 8) | (blue << 0);
136 		memcpy (base, &pixel, sizeof (uint32_t));
137 	}
138 }
139 
140 static void
fill_png_error(GError ** error,const gchar * image_uri,const gchar * msg)141 fill_png_error (GError      **error,
142 		const gchar  *image_uri,
143 		const gchar  *msg)
144 {
145 	if (msg) {
146 		g_set_error (error,
147 			     GXPS_ERROR,
148 			     GXPS_ERROR_IMAGE,
149 			     "Error loading PNG image %s: %s",
150 			     image_uri, msg);
151 	} else {
152 		g_set_error (error,
153 			     GXPS_ERROR,
154 			     GXPS_ERROR_IMAGE,
155 			     "Error loading PNG image %s",
156 			     image_uri);
157 	}
158 }
159 
160 #endif	/* HAVE_LIBPNG */
161 
162 /* Adapted from cairo's read_png in cairo-png.c
163  * http://cairographics.org/ */
164 static GXPSImage *
gxps_images_create_from_png(GXPSArchive * zip,const gchar * image_uri,GError ** error)165 gxps_images_create_from_png (GXPSArchive *zip,
166 			     const gchar *image_uri,
167 			     GError     **error)
168 {
169 #ifdef HAVE_LIBPNG
170 	GInputStream  *stream;
171 	GXPSImage     *image = NULL;
172 	char          *png_err_msg = NULL;
173 	png_struct    *png;
174 	png_info      *info;
175 	png_byte      *data = NULL;
176 	png_byte     **row_pointers = NULL;
177 	png_uint_32    png_width, png_height;
178 	int            depth, color_type, interlace, stride;
179 	unsigned int   i;
180 	cairo_format_t format;
181 	cairo_status_t status;
182 
183 	stream = gxps_archive_open (zip, image_uri);
184 	if (!stream) {
185 		g_set_error (error,
186 			     GXPS_ERROR,
187 			     GXPS_ERROR_SOURCE_NOT_FOUND,
188 			     "Image source %s not found in archive",
189 			     image_uri);
190 		return NULL;
191 	}
192 
193 	png = png_create_read_struct (PNG_LIBPNG_VER_STRING,
194 				      &png_err_msg,
195 				      png_error_callback,
196 				      png_warning_callback);
197 	if (png == NULL) {
198 		fill_png_error (error, image_uri, NULL);
199 		g_object_unref (stream);
200 		return NULL;
201 	}
202 
203 	info = png_create_info_struct (png);
204 	if (info == NULL) {
205 		fill_png_error (error, image_uri, NULL);
206 		g_object_unref (stream);
207 		png_destroy_read_struct (&png, NULL, NULL);
208 		return NULL;
209 	}
210 
211 	png_set_read_fn (png, stream, _read_png);
212 
213 	if (setjmp (png_jmpbuf (png))) {
214 		fill_png_error (error, image_uri, png_err_msg);
215 		g_free (png_err_msg);
216 		g_object_unref (stream);
217 		png_destroy_read_struct (&png, &info, NULL);
218 		gxps_image_free (image);
219 		g_free (row_pointers);
220 		g_free (data);
221 		return NULL;
222 	}
223 
224 	png_read_info (png, info);
225 
226 	png_get_IHDR (png, info,
227 		      &png_width, &png_height, &depth,
228 		      &color_type, &interlace, NULL, NULL);
229 
230 	/* convert palette/gray image to rgb */
231 	if (color_type == PNG_COLOR_TYPE_PALETTE)
232 		png_set_palette_to_rgb (png);
233 
234 	/* expand gray bit depth if needed */
235 	if (color_type == PNG_COLOR_TYPE_GRAY)
236 		png_set_expand_gray_1_2_4_to_8 (png);
237 
238 	/* transform transparency to alpha */
239 	if (png_get_valid (png, info, PNG_INFO_tRNS))
240 		png_set_tRNS_to_alpha (png);
241 
242 	if (depth == 16)
243 		png_set_strip_16 (png);
244 
245 	if (depth < 8)
246 		png_set_packing (png);
247 
248 	/* convert grayscale to RGB */
249 	if (color_type == PNG_COLOR_TYPE_GRAY ||
250 	    color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
251 		png_set_gray_to_rgb (png);
252 
253 	if (interlace != PNG_INTERLACE_NONE)
254 		png_set_interlace_handling (png);
255 
256 	png_set_filler (png, 0xff, PNG_FILLER_AFTER);
257 
258 	/* recheck header after setting EXPAND options */
259 	png_read_update_info (png, info);
260 	png_get_IHDR (png, info,
261 		      &png_width, &png_height, &depth,
262 		      &color_type, &interlace, NULL, NULL);
263 	if (depth != 8 ||
264 	    !(color_type == PNG_COLOR_TYPE_RGB ||
265               color_type == PNG_COLOR_TYPE_RGB_ALPHA)) {
266 		fill_png_error (error, image_uri, NULL);
267 		g_object_unref (stream);
268 		png_destroy_read_struct (&png, &info, NULL);
269 		return NULL;
270 	}
271 
272 	switch (color_type) {
273 	default:
274 		g_assert_not_reached();
275 		/* fall-through just in case ;-) */
276 
277 	case PNG_COLOR_TYPE_RGB_ALPHA:
278 		format = CAIRO_FORMAT_ARGB32;
279 		png_set_read_user_transform_fn (png, premultiply_data);
280 		break;
281 
282 	case PNG_COLOR_TYPE_RGB:
283 		format = CAIRO_FORMAT_RGB24;
284 		png_set_read_user_transform_fn (png, convert_bytes_to_data);
285 		break;
286 	}
287 
288 	stride = cairo_format_stride_for_width (format, png_width);
289 	if (stride < 0 || png_height >= INT_MAX / stride) {
290 		fill_png_error (error, image_uri, NULL);
291 		g_object_unref (stream);
292 		png_destroy_read_struct (&png, &info, NULL);
293 		return NULL;
294 	}
295 
296 	image = g_slice_new0 (GXPSImage);
297 	image->res_x = png_get_x_pixels_per_meter (png, info) * METERS_PER_INCH;
298 	if (image->res_x == 0)
299 		image->res_x = 96;
300 	image->res_y = png_get_y_pixels_per_meter (png, info) * METERS_PER_INCH;
301 	if (image->res_y == 0)
302 		image->res_y = 96;
303 
304 	data = g_malloc (png_height * stride);
305 	row_pointers = g_new (png_byte *, png_height);
306 
307 	for (i = 0; i < png_height; i++)
308 		row_pointers[i] = &data[i * stride];
309 
310 	png_read_image (png, row_pointers);
311 	png_read_end (png, info);
312 	png_destroy_read_struct (&png, &info, NULL);
313 	g_object_unref (stream);
314 	g_free (row_pointers);
315 
316 	image->surface = cairo_image_surface_create_for_data (data, format,
317 							      png_width, png_height,
318 							      stride);
319 	if (cairo_surface_status (image->surface)) {
320 		fill_png_error (error, image_uri, NULL);
321 		gxps_image_free (image);
322 		g_free (data);
323 		return NULL;
324 	}
325 
326 	status = cairo_surface_set_user_data (image->surface,
327 					      &image_data_cairo_key,
328 					      data,
329 					      (cairo_destroy_func_t) g_free);
330 	if (status) {
331 		fill_png_error (error, image_uri, NULL);
332 		gxps_image_free (image);
333 		g_free (data);
334 		return NULL;
335 	}
336 
337 	return image;
338 #else
339     return NULL;
340 #endif  /* HAVE_LIBPNG */
341 }
342 
343 /* JPEG */
344 #ifdef HAVE_LIBJPEG
345 #define JPEG_PROG_BUF_SIZE 65536
346 
347 struct _jpeg_src_mgr {
348 	struct jpeg_source_mgr pub;
349 	GInputStream *stream;
350 	JOCTET *buffer;
351 	jmp_buf setjmp_buffer;
352 };
353 
354 #ifdef GXPS_ENABLE_DEBUG
355 static const gchar *
_jpeg_color_space_name(const J_COLOR_SPACE jpeg_color_space)356 _jpeg_color_space_name (const J_COLOR_SPACE jpeg_color_space)
357 {
358 	switch (jpeg_color_space) {
359 	case JCS_UNKNOWN: return "UNKNOWN";
360 	case JCS_GRAYSCALE: return "GRAYSCALE";
361 	case JCS_RGB: return "RGB";
362 	case JCS_YCbCr: return "YCbCr";
363 	case JCS_CMYK: return "CMYK";
364 	case JCS_YCCK: return "YCCK";
365 	default: return "invalid";
366 	}
367 }
368 #endif
369 
370 static void
_jpeg_init_source(j_decompress_ptr cinfo)371 _jpeg_init_source (j_decompress_ptr cinfo)
372 {
373 }
374 
375 static boolean
_jpeg_fill_input_buffer(j_decompress_ptr cinfo)376 _jpeg_fill_input_buffer (j_decompress_ptr cinfo)
377 {
378 	struct _jpeg_src_mgr *src = (struct _jpeg_src_mgr *)cinfo->src;
379 	gssize num_bytes;
380 
381 	num_bytes = g_input_stream_read (src->stream, src->buffer, JPEG_PROG_BUF_SIZE, NULL, NULL);
382 	if (num_bytes <= 0) {
383 		/* Insert a fake EOI marker */
384 		src->buffer[0] = (JOCTET) 0xFF;
385 		src->buffer[1] = (JOCTET) JPEG_EOI;
386 	}
387 
388 	src->pub.next_input_byte = src->buffer;
389 	src->pub.bytes_in_buffer = num_bytes;
390 
391 	return TRUE;
392 }
393 
394 static void
_jpeg_skip_input_data(j_decompress_ptr cinfo,long num_bytes)395 _jpeg_skip_input_data (j_decompress_ptr cinfo, long num_bytes)
396 {
397 	struct _jpeg_src_mgr *src = (struct _jpeg_src_mgr *)cinfo->src;
398 
399 	if (num_bytes > 0) {
400 		while (num_bytes > (long) src->pub.bytes_in_buffer) {
401 			num_bytes -= (long) src->pub.bytes_in_buffer;
402 			_jpeg_fill_input_buffer (cinfo);
403 		}
404 		src->pub.next_input_byte += (size_t) num_bytes;
405 		src->pub.bytes_in_buffer -= (size_t) num_bytes;
406 	}
407 }
408 
409 static void
_jpeg_term_source(j_decompress_ptr cinfo)410 _jpeg_term_source (j_decompress_ptr cinfo)
411 {
412 }
413 
414 static void
_jpeg_error_exit(j_common_ptr error)415 _jpeg_error_exit (j_common_ptr error)
416 {
417 	j_decompress_ptr cinfo = (j_decompress_ptr)error;
418 	struct _jpeg_src_mgr *src = (struct _jpeg_src_mgr *)cinfo->src;
419 
420 	longjmp (src->setjmp_buffer, 1);
421 }
422 #endif /* HAVE_LIBJPEG */
423 
424 static GXPSImage *
gxps_images_create_from_jpeg(GXPSArchive * zip,const gchar * image_uri,GError ** error)425 gxps_images_create_from_jpeg (GXPSArchive *zip,
426 			      const gchar *image_uri,
427 			      GError     **error)
428 {
429 #ifdef HAVE_LIBJPEG
430 	GInputStream                 *stream;
431 	struct jpeg_error_mgr         error_mgr;
432 	struct jpeg_decompress_struct cinfo;
433 	struct _jpeg_src_mgr          src;
434 	GXPSImage                    *image;
435 	guchar                       *data;
436 	gint                          stride;
437 	JSAMPARRAY                    lines;
438 	gint                          jpeg_stride;
439 	gint                          i;
440 
441 	stream = gxps_archive_open (zip, image_uri);
442 	if (!stream) {
443 		g_set_error (error,
444 			     GXPS_ERROR,
445 			     GXPS_ERROR_SOURCE_NOT_FOUND,
446 			     "Image source %s not found in archive",
447 			     image_uri);
448 		return NULL;
449 	}
450 
451 	jpeg_std_error (&error_mgr);
452 	error_mgr.error_exit = _jpeg_error_exit;
453 
454 	jpeg_create_decompress (&cinfo);
455 	cinfo.err = &error_mgr;
456 
457 	src.stream = stream;
458 	src.buffer = (JOCTET *)	(*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT,
459 							   JPEG_PROG_BUF_SIZE * sizeof (JOCTET));
460 
461 	src.pub.init_source = _jpeg_init_source;
462 	src.pub.fill_input_buffer = _jpeg_fill_input_buffer;
463 	src.pub.skip_input_data = _jpeg_skip_input_data;
464 	src.pub.resync_to_restart = jpeg_resync_to_restart;
465 	src.pub.term_source = _jpeg_term_source;
466 	src.pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
467 	src.pub.next_input_byte = NULL; /* until buffer loaded */
468 	cinfo.src = (struct jpeg_source_mgr *)&src;
469 
470 	if (setjmp (src.setjmp_buffer)) {
471 		g_set_error (error,
472 			     GXPS_ERROR,
473 			     GXPS_ERROR_IMAGE,
474 			     "Error loading JPEG image %s",
475 			     image_uri);
476 		g_object_unref (stream);
477 		return NULL;
478 	}
479 
480 	jpeg_read_header (&cinfo, TRUE);
481 
482 	cinfo.do_fancy_upsampling = FALSE;
483 	jpeg_start_decompress (&cinfo);
484 
485 	image = g_slice_new (GXPSImage);
486 	image->surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
487 						     cinfo.output_width,
488 						     cinfo.output_height);
489 	image->res_x = 96;
490 	image->res_y = 96;
491 	if (cairo_surface_status (image->surface)) {
492 		g_set_error (error,
493 			     GXPS_ERROR,
494 			     GXPS_ERROR_IMAGE,
495 			     "Error loading JPEG image %s: %s",
496 			     image_uri,
497 			     cairo_status_to_string (cairo_surface_status (image->surface)));
498 		jpeg_destroy_decompress (&cinfo);
499 		gxps_image_free (image);
500 		g_object_unref (stream);
501 
502 		return NULL;
503 	}
504 
505 	data = cairo_image_surface_get_data (image->surface);
506 	stride = cairo_image_surface_get_stride (image->surface);
507 	jpeg_stride = cinfo.output_width * cinfo.out_color_components;
508 	lines = cinfo.mem->alloc_sarray((j_common_ptr) &cinfo, JPOOL_IMAGE, jpeg_stride, 4);
509 
510 	while (cinfo.output_scanline < cinfo.output_height) {
511 		gint n_lines, x;
512 
513 		n_lines = jpeg_read_scanlines (&cinfo, lines, cinfo.rec_outbuf_height);
514 		for (i = 0; i < n_lines; i++) {
515 			JSAMPLE *line = lines[i];
516 			guchar  *p = data;
517 
518 			for (x = 0; x < cinfo.output_width; x++) {
519 				switch (cinfo.out_color_space) {
520 				case JCS_RGB:
521 					p[0] = line[2];
522 					p[1] = line[1];
523 					p[2] = line[0];
524 					p[3] = 0xff;
525 					break;
526                                 case JCS_GRAYSCALE:
527                                         p[0] = line[0];
528                                         p[1] = line[0];
529                                         p[2] = line[0];
530                                         p[3] = 0xff;
531                                         break;
532 				default:
533 					GXPS_DEBUG (g_message ("Unsupported jpeg color space %s",
534                                                                _jpeg_color_space_name (cinfo.out_color_space)));
535 
536 					gxps_image_free (image);
537 					jpeg_destroy_decompress (&cinfo);
538 					g_object_unref (stream);
539 					return NULL;
540 				}
541 				line += cinfo.out_color_components;
542 				p += 4;
543 			}
544 
545 			data += stride;
546 		}
547 	}
548 
549 	if (cinfo.density_unit == 1) { /* dots/inch */
550 		image->res_x = cinfo.X_density;
551 		image->res_y = cinfo.Y_density;
552 	} else if (cinfo.density_unit == 2) { /* dots/cm */
553 		image->res_x = cinfo.X_density * CENTIMETERS_PER_INCH;
554 		image->res_y = cinfo.Y_density * CENTIMETERS_PER_INCH;
555 	}
556 
557 	jpeg_finish_decompress (&cinfo);
558 	jpeg_destroy_decompress (&cinfo);
559 	g_object_unref (stream);
560 
561 	cairo_surface_mark_dirty (image->surface);
562 
563 	if (cairo_surface_status (image->surface)) {
564 		g_set_error (error,
565 			     GXPS_ERROR,
566 			     GXPS_ERROR_IMAGE,
567 			     "Error loading JPEG image %s: %s",
568 			     image_uri,
569 			     cairo_status_to_string (cairo_surface_status (image->surface)));
570 		gxps_image_free (image);
571 
572 		return NULL;
573 	}
574 
575 	return image;
576 #else
577 	return NULL;
578 #endif /* HAVE_LIBJPEG */
579 }
580 
581 /* Tiff */
582 #ifdef HAVE_LIBTIFF
583 static TIFFErrorHandler orig_error_handler = NULL;
584 static TIFFErrorHandler orig_warning_handler = NULL;
585 static gchar *_tiff_error = NULL;
586 
587 typedef struct {
588 	guchar *buffer;
589 	gsize   buffer_len;
590 	guint   pos;
591 } TiffBuffer;
592 
593 static void
fill_tiff_error(GError ** error,const gchar * image_uri)594 fill_tiff_error (GError     **error,
595 		 const gchar *image_uri)
596 {
597 	if (_tiff_error) {
598 		g_set_error (error,
599 			     GXPS_ERROR,
600 			     GXPS_ERROR_IMAGE,
601 			     "Error loading TIFF image %s: %s",
602 			     image_uri, _tiff_error);
603 		g_free (_tiff_error);
604 		_tiff_error = NULL;
605 	} else {
606 		g_set_error (error,
607 			     GXPS_ERROR,
608 			     GXPS_ERROR_IMAGE,
609 			     "Error loading TIFF image %s",
610 			     image_uri);
611 	}
612 }
613 
614 G_GNUC_PRINTF (2, 0)
615 static void
_tiff_error_handler(const char * mod,const char * fmt,va_list ap)616 _tiff_error_handler (const char *mod,
617 		     const char *fmt,
618 		     va_list     ap)
619 {
620 	if (G_UNLIKELY (_tiff_error))
621 		return;
622 
623 	_tiff_error = g_strdup_vprintf (fmt, ap);
624 }
625 
626 static void
_tiff_push_handlers(void)627 _tiff_push_handlers (void)
628 {
629 	orig_error_handler = TIFFSetErrorHandler (_tiff_error_handler);
630 	orig_warning_handler = TIFFSetWarningHandler (NULL);
631 }
632 
633 static void
_tiff_pop_handlers(void)634 _tiff_pop_handlers (void)
635 {
636 	TIFFSetErrorHandler (orig_error_handler);
637 	TIFFSetWarningHandler (orig_warning_handler);
638 }
639 
640 static tsize_t
_tiff_read(thandle_t handle,tdata_t buf,tsize_t size)641 _tiff_read (thandle_t handle,
642 	    tdata_t   buf,
643 	    tsize_t   size)
644 {
645 	TiffBuffer *buffer = (TiffBuffer *)handle;
646 
647 	if (buffer->pos + size > buffer->buffer_len)
648 		return 0;
649 
650 	memcpy (buf, buffer->buffer + buffer->pos, size);
651 	buffer->pos += size;
652 
653 	return size;
654 }
655 
656 static tsize_t
_tiff_write(thandle_t handle,tdata_t buf,tsize_t size)657 _tiff_write (thandle_t handle,
658 	     tdata_t   buf,
659 	     tsize_t   size)
660 {
661 	return -1;
662 }
663 
664 static toff_t
_tiff_seek(thandle_t handle,toff_t offset,int whence)665 _tiff_seek (thandle_t handle,
666 	    toff_t    offset,
667 	    int       whence)
668 {
669 	TiffBuffer *buffer = (TiffBuffer *)handle;
670 
671 	switch (whence) {
672 	case SEEK_SET:
673 		if (offset > buffer->buffer_len)
674 			return -1;
675 		buffer->pos = offset;
676 		break;
677 	case SEEK_CUR:
678 		if (offset + buffer->pos >= buffer->buffer_len)
679 			return -1;
680 		buffer->pos += offset;
681 		break;
682 	case SEEK_END:
683 		if (offset + buffer->buffer_len > buffer->buffer_len)
684 			return -1;
685 		buffer->pos = buffer->buffer_len + offset;
686 		break;
687 	default:
688 		return -1;
689 	}
690 
691 	return buffer->pos;
692 }
693 
694 static int
_tiff_close(thandle_t context)695 _tiff_close (thandle_t context)
696 {
697 	return 0;
698 }
699 
700 static toff_t
_tiff_size(thandle_t handle)701 _tiff_size (thandle_t handle)
702 {
703 	TiffBuffer *buffer = (TiffBuffer *)handle;
704 
705 	return buffer->buffer_len;
706 }
707 
708 static int
_tiff_map_file(thandle_t handle,tdata_t * buf,toff_t * size)709 _tiff_map_file (thandle_t handle,
710 		tdata_t  *buf,
711 		toff_t   *size)
712 {
713 	TiffBuffer *buffer = (TiffBuffer *)handle;
714 
715 	*buf = buffer->buffer;
716 	*size = buffer->buffer_len;
717 
718 	return 0;
719 }
720 
721 static void
_tiff_unmap_file(thandle_t handle,tdata_t data,toff_t offset)722 _tiff_unmap_file (thandle_t handle,
723 		  tdata_t   data,
724 		  toff_t    offset)
725 {
726 }
727 #endif /* #ifdef HAVE_LIBTIFF */
728 
729 static GXPSImage *
gxps_images_create_from_tiff(GXPSArchive * zip,const gchar * image_uri,GError ** error)730 gxps_images_create_from_tiff (GXPSArchive *zip,
731 			      const gchar *image_uri,
732 			      GError     **error)
733 {
734 #ifdef HAVE_LIBTIFF
735 	TIFF       *tiff;
736 	TiffBuffer  buffer;
737 	GXPSImage  *image;
738 	gint        width, height;
739 	guint16     res_unit;
740 	float       res_x, res_y;
741 	gint        stride;
742 	guchar     *data;
743 	guchar     *p;
744 
745         if (!gxps_archive_read_entry (zip, image_uri,
746                                       &buffer.buffer,
747                                       &buffer.buffer_len,
748                                       error)) {
749                 return NULL;
750         }
751 
752 	buffer.pos = 0;
753 
754 	_tiff_push_handlers ();
755 
756 	tiff = TIFFClientOpen ("libgxps-tiff", "r", &buffer,
757 			       _tiff_read,
758 			       _tiff_write,
759 			       _tiff_seek,
760 			       _tiff_close,
761 			       _tiff_size,
762 			       _tiff_map_file,
763 			       _tiff_unmap_file);
764 
765 	if (!tiff || _tiff_error) {
766 		fill_tiff_error (error, image_uri);
767 		if (tiff)
768 			TIFFClose (tiff);
769 		_tiff_pop_handlers ();
770 		g_free (buffer.buffer);
771 		return NULL;
772 	}
773 
774 	if (!TIFFGetField (tiff, TIFFTAG_IMAGEWIDTH, &width) || _tiff_error) {
775 		fill_tiff_error (error, image_uri);
776 		TIFFClose (tiff);
777 		_tiff_pop_handlers ();
778 		g_free (buffer.buffer);
779 		return NULL;
780 	}
781 
782 	if (!TIFFGetField (tiff, TIFFTAG_IMAGELENGTH, &height) || _tiff_error) {
783 		fill_tiff_error (error, image_uri);
784 		TIFFClose (tiff);
785 		_tiff_pop_handlers ();
786 		g_free (buffer.buffer);
787 		return NULL;
788 	}
789 
790 	if (width <= 0 || height <= 0) {
791 		fill_tiff_error (error, image_uri);
792 		TIFFClose (tiff);
793 		_tiff_pop_handlers ();
794 		g_free (buffer.buffer);
795 		return NULL;
796 	}
797 
798 	image = g_slice_new (GXPSImage);
799 	image->surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
800 						     width, height);
801 	image->res_x = 96;
802 	image->res_y = 96;
803 
804 	if (!TIFFGetField (tiff, TIFFTAG_RESOLUTIONUNIT, &res_unit))
805 		res_unit = 0;
806 	if (TIFFGetField (tiff, TIFFTAG_XRESOLUTION, &res_x)) {
807 		if (res_unit == 2) { /* inches */
808 			image->res_x = res_x;
809 		} else if (res_unit == 3) { /* centimeters */
810 			image->res_x = res_x * CENTIMETERS_PER_INCH;
811 		}
812 	}
813 	if (TIFFGetField (tiff, TIFFTAG_YRESOLUTION, &res_y)) {
814 		if (res_unit == 2) { /* inches */
815 			image->res_y = res_y;
816 		} else if (res_unit == 3) { /* centimeters */
817 			image->res_y = res_y * CENTIMETERS_PER_INCH;
818 		}
819 	}
820 
821 	if (cairo_surface_status (image->surface)) {
822 		g_set_error (error,
823 			     GXPS_ERROR,
824 			     GXPS_ERROR_IMAGE,
825 			     "Error loading TIFF image %s: %s",
826 			     image_uri,
827 			     cairo_status_to_string (cairo_surface_status (image->surface)));
828 		gxps_image_free (image);
829 		TIFFClose (tiff);
830 		_tiff_pop_handlers ();
831 		g_free (buffer.buffer);
832 		return NULL;
833 	}
834 
835 	data = cairo_image_surface_get_data (image->surface);
836 	if (!TIFFReadRGBAImageOriented (tiff, width, height,
837 					(uint32 *)data,
838 					ORIENTATION_TOPLEFT, 1) || _tiff_error) {
839 		fill_tiff_error (error, image_uri);
840 		gxps_image_free (image);
841 		TIFFClose (tiff);
842 		_tiff_pop_handlers ();
843 		g_free (buffer.buffer);
844 		return NULL;
845 	}
846 
847 	TIFFClose (tiff);
848 	_tiff_pop_handlers ();
849 	g_free (buffer.buffer);
850 
851 	stride = cairo_image_surface_get_stride (image->surface);
852 	p = data;
853 	while (p < data + (height * stride)) {
854 		guint32 *pixel = (guint32 *)p;
855 		guint8   r = TIFFGetR (*pixel);
856 		guint8   g = TIFFGetG (*pixel);
857 		guint8   b = TIFFGetB (*pixel);
858 		guint8   a = TIFFGetA (*pixel);
859 
860 		*pixel = (a << 24) | (r << 16) | (g << 8) | b;
861 
862 		p += 4;
863 	}
864 
865 	cairo_surface_mark_dirty (image->surface);
866 
867 	return image;
868 #else
869 	return NULL;
870 #endif /* #ifdef HAVE_LIBTIFF */
871 }
872 
873 static gchar *
gxps_images_guess_content_type(GXPSArchive * zip,const gchar * image_uri)874 gxps_images_guess_content_type (GXPSArchive *zip,
875 				const gchar *image_uri)
876 {
877 	GInputStream *stream;
878 	guchar        buffer[1024];
879 	gssize        bytes_read;
880 	gchar        *mime_type;
881 
882 	stream = gxps_archive_open (zip, image_uri);
883 	if (!stream)
884 		return NULL;
885 
886 	bytes_read = g_input_stream_read (stream, buffer, 1024, NULL, NULL);
887 	mime_type = g_content_type_guess (NULL, buffer, bytes_read, NULL);
888 	g_object_unref (stream);
889 
890 	return mime_type;
891 }
892 
893 GXPSImage *
gxps_images_get_image(GXPSArchive * zip,const gchar * image_uri,GError ** error)894 gxps_images_get_image (GXPSArchive *zip,
895 		       const gchar *image_uri,
896 		       GError     **error)
897 {
898 	GXPSImage *image = NULL;
899 	gchar *image_uri_lower;
900 
901 	/* First try with extensions,
902 	 * as it's recommended by the spec
903 	 * (2.1.5 Image Parts)
904 	 */
905 	image_uri_lower = g_utf8_strdown (image_uri, -1);
906 	if (g_str_has_suffix (image_uri_lower, ".png")) {
907 		image = gxps_images_create_from_png (zip, image_uri, error);
908 	} else if (g_str_has_suffix (image_uri_lower, ".jpg")) {
909 		image = gxps_images_create_from_jpeg (zip, image_uri, error);
910 	} else if (g_str_has_suffix (image_uri_lower, ".tif")) {
911 		image = gxps_images_create_from_tiff (zip, image_uri, error);
912 	} else if (g_str_has_suffix (image_uri_lower, "wdp")) {
913 		GXPS_DEBUG (g_message ("Unsupported image format windows media photo"));
914 		g_free (image_uri_lower);
915 		return NULL;
916 	}
917 
918 	g_free (image_uri_lower);
919 
920 	if (!image) {
921 		gchar *mime_type;
922 
923                 g_clear_error(error);
924 
925 		mime_type = gxps_images_guess_content_type (zip, image_uri);
926 		if (g_strcmp0 (mime_type, "image/png") == 0) {
927 			image = gxps_images_create_from_png (zip, image_uri, error);
928 		} else if (g_strcmp0 (mime_type, "image/jpeg") == 0) {
929 			image = gxps_images_create_from_jpeg (zip, image_uri, error);
930 		} else if (g_strcmp0 (mime_type, "image/tiff") == 0) {
931 			image = gxps_images_create_from_tiff (zip, image_uri, error);
932 		} else {
933 			GXPS_DEBUG (g_message ("Unsupported image format: %s", mime_type));
934 		}
935 		g_free (mime_type);
936 	}
937 
938 	return image;
939 }
940 
941 void
gxps_image_free(GXPSImage * image)942 gxps_image_free (GXPSImage *image)
943 {
944 	if (G_UNLIKELY (!image))
945 		return;
946 
947 	if (G_LIKELY (image->surface))
948 		cairo_surface_destroy (image->surface);
949 
950 	g_slice_free (GXPSImage, image);
951 }
952