1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  GThumb
5  *
6  *  Copyright (C) 2011-2015 Free Software Foundation, Inc.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 
23 #include <config.h>
24 #include "jpeg-info.h"
25 
26 
27 void
_jpeg_info_data_init(JpegInfoData * data)28 _jpeg_info_data_init (JpegInfoData *data)
29 {
30 	data->valid = _JPEG_INFO_NONE;
31 	data->width = 0;
32 	data->height = 0;
33 	data->orientation = GTH_TRANSFORM_NONE;
34 	data->icc_data = NULL;
35 	data->icc_data_size = 0;
36 }
37 
38 
39 void
_jpeg_info_data_dispose(JpegInfoData * data)40 _jpeg_info_data_dispose (JpegInfoData *data)
41 {
42 	if (data->valid & _JPEG_INFO_ICC_PROFILE)
43 		g_free (data->icc_data);
44 }
45 
46 
47 static guchar
_g_input_stream_read_byte(GInputStream * stream,GCancellable * cancellable,GError ** error)48 _g_input_stream_read_byte (GInputStream  *stream,
49 			   GCancellable  *cancellable,
50 			   GError       **error)
51 {
52 	guchar v;
53 	return (g_input_stream_read (stream, &v, 1, cancellable, error) > 0) ? v : 0;
54 }
55 
56 
57 static guchar
_jpeg_read_segment_marker(GInputStream * stream,GCancellable * cancellable,GError ** error)58 _jpeg_read_segment_marker (GInputStream  *stream,
59 			   GCancellable  *cancellable,
60 			   GError       **error)
61 {
62 	guchar marker_id;
63 
64 	if (_g_input_stream_read_byte (stream, cancellable, error) != 0xff)
65 		return 0x00;
66 
67 	while ((marker_id = _g_input_stream_read_byte (stream, cancellable, error)) == 0xff)
68 		/* skip padding */;
69 
70 	return marker_id;
71 }
72 
73 
74 static gboolean
_jpeg_skip_segment_data(GInputStream * stream,guchar marker_id,GCancellable * cancellable,GError ** error)75 _jpeg_skip_segment_data (GInputStream  *stream,
76 			 guchar         marker_id,
77 			 GCancellable  *cancellable,
78 			 GError       **error)
79 {
80 	if (marker_id == 0xd9)  /* EOI => end of image */
81 		return FALSE;
82 	if (marker_id == 0xda)  /* SOS => end of header */
83 		return FALSE;
84 
85 	if ((marker_id != 0xd0)
86 	    && (marker_id != 0xd1)
87 	    && (marker_id != 0xd2)
88 	    && (marker_id != 0xd3)
89 	    && (marker_id != 0xd4)
90 	    && (marker_id != 0xd5)
91 	    && (marker_id != 0xd6)
92 	    && (marker_id != 0xd7)
93 	    && (marker_id != 0xd8)
94 	    && (marker_id != 0x01))
95 	{
96 		guint h, l;
97 		guint segment_size;
98 
99 		/* skip to the next segment */
100 
101 		h = _g_input_stream_read_byte (stream, cancellable, error);
102 		l = _g_input_stream_read_byte (stream, cancellable, error);
103 		segment_size = (h << 8) + l;
104 
105 		if (g_input_stream_skip (stream, segment_size - 2, cancellable, error) < 0)
106 			return FALSE;
107 	}
108 
109 	return TRUE;
110 }
111 
112 
113 static gboolean
_jpeg_exif_tags_from_app1_segment(guchar * in_buffer,gsize app1_segment_size,JpegInfoFlags flags,JpegInfoData * data)114 _jpeg_exif_tags_from_app1_segment (guchar	 *in_buffer,
115 				   gsize	  app1_segment_size,
116 				   JpegInfoFlags  flags,
117 				   JpegInfoData	 *data)
118 {
119 	int       pos;
120 	guint     length;
121 	gboolean  big_endian;
122 	guchar   *exif_data;
123 	guint     offset, number_of_tags, tagnum;
124 	int       remaining_tags;
125 
126 	/* Length includes itself, so must be at least 2 */
127 	/* Following Exif data length must be at least 6 */
128 
129 	length = app1_segment_size;
130 	if (length < 6)
131 		return FALSE;
132 
133 	pos = 0;
134 
135 	/* Read Exif head, check for "Exif" */
136 
137 	if ((in_buffer[pos++] != 'E')
138 	    || (in_buffer[pos++] != 'x')
139 	    || (in_buffer[pos++] != 'i')
140 	    || (in_buffer[pos++] != 'f')
141 	    || (in_buffer[pos++] != 0)
142 	    || (in_buffer[pos++] != 0))
143 	{
144 		return FALSE;
145 	}
146 
147 	/* Length of an IFD entry */
148 
149 	if (length < 12)
150 		return FALSE;
151 
152 	exif_data = in_buffer + pos;
153 
154 	/* Discover byte order */
155 
156 	if ((exif_data[0] == 0x49) && (exif_data[1] == 0x49))
157 		big_endian = FALSE;
158 	else if ((exif_data[0] == 0x4D) && (exif_data[1] == 0x4D))
159 		big_endian = TRUE;
160 	else
161 		return FALSE;
162 
163 	/* Check Tag Mark */
164 
165 	if (big_endian) {
166 		if (exif_data[2] != 0)
167 			return FALSE;
168 		if (exif_data[3] != 0x2A)
169 			return FALSE;
170 	}
171 	else {
172 		if (exif_data[3] != 0)
173 			return FALSE;
174 		if (exif_data[2] != 0x2A)
175 			return FALSE;
176 	}
177 
178 	/* Get first IFD offset (offset to IFD0) */
179 
180 	if (big_endian) {
181 		if (exif_data[4] != 0)
182 			return FALSE;
183 		if (exif_data[5] != 0)
184 			return FALSE;
185 		offset = (exif_data[6] << 8) + exif_data[7];
186 	}
187 	else {
188 		if (exif_data[7] != 0)
189 			return FALSE;
190 		if (exif_data[6] != 0)
191 			return FALSE;
192 		offset = (exif_data[5] << 8) + exif_data[4];
193 	}
194 
195 	if (offset > length - 2) /* check end of data segment */
196 		return FALSE;
197 
198 	/* Get the number of directory entries contained in this IFD */
199 
200 	if (big_endian)
201 		number_of_tags = (exif_data[offset] << 8) + exif_data[offset+1];
202 	else
203 		number_of_tags = (exif_data[offset+1] << 8) + exif_data[offset];
204 	if (number_of_tags == 0)
205 		return FALSE;
206 
207 	offset += 2;
208 
209 	/* Search the tags in IFD0 */
210 
211 	remaining_tags = 0;
212 	if (flags & _JPEG_INFO_EXIF_ORIENTATION)
213 		remaining_tags += 1;
214 	if (flags & _JPEG_INFO_EXIF_COLORIMETRY)
215 		remaining_tags += 3;
216 	if (flags & _JPEG_INFO_EXIF_COLOR_SPACE)
217 		remaining_tags += 1;
218 
219 	for (;;) {
220 		if (offset > length - 12) /* check end of data segment */
221 			return FALSE;
222 
223 		/* Get Tag number */
224 
225 		if (big_endian)
226 			tagnum = (exif_data[offset] << 8) + exif_data[offset+1];
227 		else
228 			tagnum = (exif_data[offset+1] << 8) + exif_data[offset];
229 
230 		if ((flags & _JPEG_INFO_EXIF_ORIENTATION) && (tagnum == 0x0112)) { /* Orientation */
231 			int orientation;
232 
233 			if (big_endian) {
234 				if (exif_data[offset + 8] != 0)
235 					return FALSE;
236 				orientation = exif_data[offset + 9];
237 			}
238 			else {
239 				if (exif_data[offset + 9] != 0)
240 					return FALSE;
241 				orientation = exif_data[offset + 8];
242 			}
243 			if (orientation > 8)
244 				orientation = 0;
245 			data->orientation = orientation;
246 			data->valid |= _JPEG_INFO_EXIF_ORIENTATION;
247 
248 			remaining_tags--;
249 		}
250 
251 		if ((flags & _JPEG_INFO_EXIF_COLORIMETRY) && (tagnum == 0x012D)) { /* TransferFunction */
252 			remaining_tags--;
253 		}
254 
255 		if ((flags & _JPEG_INFO_EXIF_COLORIMETRY) && (tagnum == 0x013E)) { /* WhitePoint */
256 			remaining_tags--;
257 		}
258 
259 		if ((flags & _JPEG_INFO_EXIF_COLORIMETRY) && (tagnum == 0x013F)) { /* PrimaryChromaticities */
260 			remaining_tags--;
261 		}
262 
263 		if ((flags & _JPEG_INFO_EXIF_COLOR_SPACE) && (tagnum == 0xA001)) { /* ColorSpace */
264 			int value;
265 
266 			if (big_endian) {
267 				if (exif_data[offset + 8] != 0)
268 					return FALSE;
269 				value = exif_data[offset + 9];
270 			}
271 			else {
272 				if (exif_data[offset + 9] != 0)
273 					return FALSE;
274 				value = exif_data[offset + 8];
275 			}
276 
277 			if (value == 1)
278 				data->color_space = GTH_COLOR_SPACE_SRGB;
279 			else if (value == 0xFFFF)
280 				data->color_space = GTH_COLOR_SPACE_UNCALIBRATED;
281 			else
282 				data->color_space = GTH_COLOR_SPACE_UNKNOWN;
283 			data->valid |= _JPEG_INFO_EXIF_COLOR_SPACE;
284 
285 			remaining_tags--;
286 		}
287 
288 		if (remaining_tags == 0)
289 			break;
290 
291 		if (--number_of_tags == 0)
292 			return FALSE;
293 
294 		offset += 12;
295 	}
296 
297 	return TRUE;
298 }
299 
300 
301 /* -- _jpeg_get_icc_profile_chunk_from_app2_segment -- */
302 
303 
304 typedef struct {
305 	int	 seq_n;
306 	int	 tot;
307 	guchar  *in_buffer;
308 	guchar	*data;
309 	gsize    size;
310 } ICCProfileChunk;
311 
312 
313 static void
icc_profile_chunk_free(ICCProfileChunk * chunk)314 icc_profile_chunk_free (ICCProfileChunk *chunk)
315 {
316 	g_free (chunk->in_buffer);
317 	g_free (chunk);
318 }
319 
320 
321 static int
icc_chunk_compare(gconstpointer a,gconstpointer b)322 icc_chunk_compare (gconstpointer a,
323 		   gconstpointer b)
324 {
325 	const ICCProfileChunk *chunk_a = a;
326 	const ICCProfileChunk *chunk_b = b;
327 
328 	if (chunk_a->seq_n < chunk_b->seq_n)
329 		return -1;
330 	if (chunk_a->seq_n > chunk_b->seq_n)
331 		return 1;
332 	return 0;
333 }
334 
335 
336 static ICCProfileChunk *
_jpeg_get_icc_profile_chunk_from_app2_segment(guchar * in_buffer,gsize app2_segment_size)337 _jpeg_get_icc_profile_chunk_from_app2_segment (guchar *in_buffer,
338 					       gsize   app2_segment_size)
339 {
340 	int		 pos;
341 	guint		 length;
342 	ICCProfileChunk *chunk;
343 
344 	length = app2_segment_size;
345 	if (length <= 14)
346 		return NULL;
347 
348 	pos = 0;
349 
350 	/* check for "ICC_PROFILE" */
351 
352 	if ((in_buffer[pos++] != 'I')
353 	    || (in_buffer[pos++] != 'C')
354 	    || (in_buffer[pos++] != 'C')
355 	    || (in_buffer[pos++] != '_')
356 	    || (in_buffer[pos++] != 'P')
357 	    || (in_buffer[pos++] != 'R')
358 	    || (in_buffer[pos++] != 'O')
359 	    || (in_buffer[pos++] != 'F')
360 	    || (in_buffer[pos++] != 'I')
361 	    || (in_buffer[pos++] != 'L')
362 	    || (in_buffer[pos++] != 'E')
363 	    || (in_buffer[pos++] != 0))
364 	{
365 		return NULL;
366 	}
367 
368 	chunk = g_new (ICCProfileChunk, 1);
369 	chunk->in_buffer = in_buffer;
370 	chunk->seq_n = in_buffer[pos++];
371 	chunk->tot = in_buffer[pos++];
372 	chunk->data = in_buffer + 14;
373 	chunk->size = app2_segment_size - 14;
374 
375 	return chunk;
376 }
377 
378 
379 #define _JPEG_MARKER_SOF0 0xc0
380 #define _JPEG_MARKER_SOF1 0xc2
381 #define _JPEG_MARKER_APP1 0xe1
382 #define _JPEG_MARKER_APP2 0xe2
383 
384 
385 gboolean
_jpeg_info_get_from_stream(GInputStream * stream,JpegInfoFlags flags,JpegInfoData * data,GCancellable * cancellable,GError ** error)386 _jpeg_info_get_from_stream (GInputStream	 *stream,
387 			    JpegInfoFlags	  flags,
388 			    JpegInfoData	 *data,
389 			    GCancellable	 *cancellable,
390 			    GError		**error)
391 {
392 	GList    *icc_chunks;
393 	guchar    marker_id;
394 
395 	g_return_val_if_fail (data->valid == _JPEG_INFO_NONE, FALSE);
396 
397 	icc_chunks = NULL;
398 	while ((marker_id = _jpeg_read_segment_marker (stream, cancellable, error)) != 0x00) {
399 		gboolean segment_data_consumed = FALSE;
400 
401 		if (((flags & _JPEG_INFO_IMAGE_SIZE) && ! (data->valid & _JPEG_INFO_IMAGE_SIZE))
402 		    && ((marker_id == _JPEG_MARKER_SOF0) || (marker_id == _JPEG_MARKER_SOF1)))
403 		{
404 			guint h, l;
405 			guint size;
406 
407 			/* size */
408 
409 			h = _g_input_stream_read_byte (stream, cancellable, error);
410 			l = _g_input_stream_read_byte (stream, cancellable, error);
411 			size = (h << 8) + l;
412 
413 			/* data precision */
414 
415 			(void) _g_input_stream_read_byte (stream, cancellable, error);
416 
417 			/* height */
418 
419 			h = _g_input_stream_read_byte (stream, cancellable, error);
420 			l = _g_input_stream_read_byte (stream, cancellable, error);
421 			data->height = (h << 8) + l;
422 
423 			/* width */
424 
425 			h = _g_input_stream_read_byte (stream, cancellable, error);
426 			l = _g_input_stream_read_byte (stream, cancellable, error);
427 			data->width = (h << 8) + l;
428 
429 			g_input_stream_skip (stream, size - 7, cancellable, error);
430 
431 			segment_data_consumed = TRUE;
432 		}
433 
434 		if (((flags & _JPEG_INFO_EXIF_ORIENTATION)
435 		     || (flags & _JPEG_INFO_EXIF_COLORIMETRY)
436 		     || (flags & _JPEG_INFO_EXIF_COLOR_SPACE))
437 		    && (marker_id == _JPEG_MARKER_APP1))
438 		{
439 			guint   h, l;
440 			guint   app1_segment_size;
441 			guchar *app1_segment;
442 
443 			h = _g_input_stream_read_byte (stream, cancellable, error);
444 			l = _g_input_stream_read_byte (stream, cancellable, error);
445 			app1_segment_size = (h << 8) + l - 2;
446 
447 			app1_segment = g_new (guchar, app1_segment_size);
448 			if (g_input_stream_read_all (stream,
449 						     app1_segment,
450 						     app1_segment_size,
451 						     NULL,
452 						     cancellable,
453 						     error))
454 			{
455 				_jpeg_exif_tags_from_app1_segment (app1_segment, app1_segment_size, flags, data);
456 			}
457 
458 			segment_data_consumed = TRUE;
459 
460 			g_free (app1_segment);
461 		}
462 
463 		if ((flags & _JPEG_INFO_ICC_PROFILE) && (marker_id == _JPEG_MARKER_APP2)) {
464 			guint   h, l;
465 			gsize   app2_segment_size;
466 			guchar *app2_segment;
467 
468 			/* size */
469 
470 			h = _g_input_stream_read_byte (stream, cancellable, error);
471 			l = _g_input_stream_read_byte (stream, cancellable, error);
472 			app2_segment_size = (h << 8) + l - 2;
473 
474 			app2_segment = g_new (guchar, app2_segment_size);
475 			if (g_input_stream_read_all (stream,
476 						     app2_segment,
477 						     app2_segment_size,
478 						     NULL,
479 						     cancellable,
480 						     error))
481 			{
482 				ICCProfileChunk *chunk;
483 
484 				chunk = _jpeg_get_icc_profile_chunk_from_app2_segment (app2_segment, app2_segment_size);
485 				if (chunk != NULL)
486 					icc_chunks = g_list_prepend (icc_chunks, chunk);
487 			}
488 
489 			segment_data_consumed = TRUE;
490 		}
491 
492 		if (! segment_data_consumed && ! _jpeg_skip_segment_data (stream, marker_id, cancellable, error))
493 			break;
494 	}
495 
496 	if (flags & _JPEG_INFO_ICC_PROFILE) {
497 		gboolean	 valid_icc = (icc_chunks != NULL);
498 		GOutputStream	*ostream;
499 		GList		*scan;
500 		int		 seq_n;
501 
502 		ostream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
503 		icc_chunks = g_list_sort (icc_chunks, icc_chunk_compare);
504 		seq_n = 1;
505 		for (scan = icc_chunks; scan; scan = scan->next) {
506 			ICCProfileChunk *chunk = scan->data;
507 
508 			if (chunk->seq_n != seq_n) {
509 				valid_icc = FALSE;
510 				g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid ICC data");
511 				break;
512 			}
513 
514 			g_output_stream_write_all (ostream, chunk->data, chunk->size, NULL, cancellable, error);
515 			seq_n++;
516 		}
517 
518 		if (valid_icc && g_output_stream_close (ostream, NULL, NULL)) {
519 			data->valid |= _JPEG_INFO_ICC_PROFILE;
520 			data->icc_data = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (ostream));
521 			data->icc_data_size = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (ostream));
522 		}
523 
524 		g_object_unref (ostream);
525 	}
526 
527 	g_list_free_full (icc_chunks, (GDestroyNotify) icc_profile_chunk_free);
528 
529 	return (flags == data->valid);
530 }
531 
532 
533 gboolean
_jpeg_info_get_from_buffer(guchar * in_buffer,gsize in_buffer_size,JpegInfoFlags flags,JpegInfoData * data)534 _jpeg_info_get_from_buffer (guchar		 *in_buffer,
535 			    gsize		  in_buffer_size,
536 			    JpegInfoFlags	  flags,
537 			    JpegInfoData	 *data)
538 {
539 	GInputStream *stream;
540 	gboolean      result;
541 
542 	stream = g_memory_input_stream_new_from_data (in_buffer, in_buffer_size, NULL);
543 	result = _jpeg_info_get_from_stream (stream, flags, data, NULL, NULL);
544 
545 	g_object_unref (stream);
546 
547 	return result;
548 }
549