tracker-0.16.2/src/libtracker-extract/tracker-exif.c

No issues found

  1 /*
  2  * Copyright (C) 2009, Nokia <ivan.frade@nokia.com>
  3  *
  4  * This library is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU Lesser General Public
  6  * License as published by the Free Software Foundation; either
  7  * version 2.1 of the License, or (at your option) any later version.
  8  *
  9  * This library is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 12  * Lesser General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU Lesser General Public
 15  * License along with this library; if not, write to the
 16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 17  * Boston, MA  02110-1301, USA.
 18  */
 19 
 20 #include "config.h"
 21 
 22 #include <string.h>
 23 #include <ctype.h>
 24 
 25 #include "tracker-exif.h"
 26 #include "tracker-utils.h"
 27 
 28 #ifdef HAVE_LIBEXIF
 29 
 30 #include <libexif/exif-data.h>
 31 
 32 #define EXIF_DATE_FORMAT "%Y:%m:%d %H:%M:%S"
 33 
 34 /**
 35  * SECTION:tracker-exif
 36  * @title: Exif
 37  * @short_description: Exchangeable Image File Format (EXIF)
 38  * @stability: Stable
 39  * @include: libtracker-extract/tracker-extract.h
 40  *
 41  * Exchangeable Image File Format (EXIF) is a specification for the
 42  * image file format used by digital cameras. The specification uses
 43  * the existing JPEG, TIFF Rev. 6.0, and RIFF WAV file formats, with
 44  * the addition of specific metadata tags. It is not supported in JPEG
 45  * 2000, PNG, or GIF.
 46  *
 47  * This API is provided to remove code duplication between extractors
 48  * using these standards.
 49  **/
 50 
 51 static gchar *
 52 get_date (ExifData *exif,
 53           ExifTag   tag)
 54 {
 55 	ExifEntry *entry = exif_data_get_entry (exif, tag);
 56 
 57 	if (entry) {
 58 		gchar buf[1024];
 59 
 60 		exif_entry_get_value (entry, buf, 1024);
 61 		/* From: ex; date "2007:04:15 15:35:58"
 62 		 * To  : ex. "2007-04-15T17:35:58+0200 where +0200 is offset w.r.t gmt */
 63 		return tracker_date_format_to_iso8601 (buf, EXIF_DATE_FORMAT);
 64 	}
 65 
 66 	return NULL;
 67 }
 68 
 69 static gchar *
 70 get_focal_length (ExifData *exif,
 71                   ExifTag   tag)
 72 {
 73 	ExifEntry *entry = exif_data_get_entry (exif, tag);
 74 
 75 	if (entry) {
 76 		gchar buf[1024];
 77 		const gchar *end;
 78 		exif_entry_get_value (entry, buf, 1024);
 79 		end = g_strstr_len (buf, 1024, " mm");
 80 		if (end) {
 81 			return g_strndup (buf, end - buf);
 82 		} else {
 83 			return NULL;
 84 		}
 85 	}
 86 
 87 	return NULL;
 88 }
 89 
 90 static gchar *
 91 get_flash (ExifData *exif,
 92            ExifTag   tag)
 93 {
 94 	ExifEntry *entry = exif_data_get_entry (exif, tag);
 95 
 96 	if (entry) {
 97 		ExifByteOrder order;
 98 		gushort flash;
 99 
100 		order = exif_data_get_byte_order (exif);
101 		flash = exif_get_short (entry->data, order);
102 
103 		switch (flash) {
104 		case 0x0000: /* No flash */
105 		case 0x0005: /* Without strobe */
106 		case 0x0008: /* Flash did not fire */
107 		case 0x0010: /* Flash in compulsory mode, did not fire */
108 		case 0x0018: /* Flash in auto mode, did not fire */
109 		case 0x0058: /* Only red-eye reduction mode */
110 			return g_strdup ("nmm:flash-off");
111 		default:
112 			return g_strdup ("nmm:flash-on");
113 		}
114 	}
115 
116 	return NULL;
117 }
118 
119 static gchar *
120 get_fnumber (ExifData *exif,
121              ExifTag   tag)
122 {
123 	ExifEntry *entry = exif_data_get_entry (exif, tag);
124 
125 	if (entry) {
126 		gchar buf[1024];
127 		gchar *new_fn;
128 
129 		exif_entry_get_value (entry, buf, 1024);
130 
131 		if (strlen (buf) <= 0) {
132 			return NULL;
133 		}
134 
135 		new_fn = g_strdup (buf);
136 
137 		if (new_fn[0] == 'F') {
138 			new_fn[0] = ' ';
139 		} else if (buf[0] == 'f' && new_fn[1] == '/') {
140 			new_fn[0] = new_fn[1] = ' ';
141 		}
142 
143 		return g_strstrip (new_fn);
144 	}
145 
146 	return NULL;
147 }
148 
149 static gchar *
150 get_exposure_time (ExifData *exif,
151                    ExifTag   tag)
152 {
153 	ExifEntry *entry = exif_data_get_entry (exif, tag);
154 
155 	if (entry) {
156 		gchar buf[1024];
157 		gchar *sep;
158 
159 		exif_entry_get_value (entry, buf, 1024);
160 
161 		sep = strchr (buf, '/');
162 
163 		if (sep) {
164 			gdouble fraction;
165 
166 			fraction = g_ascii_strtod (sep + 1, NULL);
167 
168 			if (fraction > 0.0) {
169 				gdouble val;
170 				gchar   bufr[G_ASCII_DTOSTR_BUF_SIZE];
171 
172 				val = 1.0f / fraction;
173 				g_ascii_dtostr (bufr, sizeof(bufr), val);
174 
175 				return g_strdup (bufr);
176 			}
177 		}
178 
179 		return g_strdup (buf);
180 	}
181 
182 	return NULL;
183 }
184 
185 static gchar *
186 get_orientation (ExifData *exif,
187                  ExifTag   tag)
188 {
189 	ExifEntry *entry = exif_data_get_entry (exif, tag);
190 
191 	if (entry) {
192 		ExifByteOrder order;
193 		gushort orientation;
194 
195 		order = exif_data_get_byte_order (exif);
196 		orientation = exif_get_short (entry->data, order);
197 
198 		switch (orientation) {
199 		case 1:
200 			return g_strdup ("nfo:orientation-top");
201 		case 2:
202 			return g_strdup ("nfo:orientation-top-mirror");
203 		case 3:
204 			return g_strdup ("nfo:orientation-bottom");
205 		case 4:
206 			return g_strdup ("nfo:orientation-bottom-mirror");
207 		case 5:
208 			return g_strdup ("nfo:orientation-left-mirror");
209 		case 6:
210 			return g_strdup ("nfo:orientation-right");
211 		case 7:
212 			return g_strdup ("nfo:orientation-right-mirror");
213 		case 8:
214 			return g_strdup ("nfo:orientation-left");
215 		default:
216 			return g_strdup ("nfo:orientation-top");
217 		}
218 	}
219 
220 	return NULL;
221 }
222 
223 static gchar *
224 get_metering_mode (ExifData *exif,
225                    ExifTag   tag)
226 {
227 	ExifEntry *entry = exif_data_get_entry (exif, tag);
228 
229 	if (entry) {
230 		ExifByteOrder order;
231 		gushort metering;
232 
233 		order = exif_data_get_byte_order (exif);
234 		metering = exif_get_short (entry->data, order);
235 
236 		switch (metering) {
237 		case 1:
238 			return g_strdup ("nmm:metering-mode-average");
239 		case 2:
240 			return g_strdup ("nmm:metering-mode-center-weighted-average");
241 		case 3:
242 			return g_strdup ("nmm:metering-mode-spot");
243 		case 4:
244 			return g_strdup ("nmm:metering-mode-multispot");
245 		case 5:
246 			return g_strdup ("nmm:metering-mode-pattern");
247 		case 6:
248 			return g_strdup ("nmm:metering-mode-partial");
249 		default:
250 			return g_strdup ("nmm:metering-mode-other");
251 		}
252 	}
253 
254 	return NULL;
255 }
256 
257 static gchar *
258 get_white_balance (ExifData *exif,
259                    ExifTag   tag)
260 {
261 	ExifEntry *entry = exif_data_get_entry (exif, tag);
262 
263 	if (entry) {
264 		ExifByteOrder order;
265 		gushort white_balance;
266 
267 		order = exif_data_get_byte_order (exif);
268 		white_balance = exif_get_short (entry->data, order);
269 
270 		if (white_balance == 0)
271 			return g_strdup ("nmm:white-balance-auto");
272 
273 		/* Found in the field: sunny, fluorescent, incandescent, cloudy.
274 		 * These will this way also yield as manual. */
275 		return g_strdup ("nmm:white-balance-manual");
276 	}
277 
278 	return NULL;
279 }
280 
281 static gchar *
282 get_gps_coordinate (ExifData *exif,
283                     ExifTag   tag,
284                     ExifTag   reftag)
285 {
286 	ExifEntry *entry = exif_data_get_entry (exif, tag);
287 	ExifEntry *refentry = exif_data_get_entry (exif, reftag);
288 
289 	if (entry && refentry) {
290 		ExifByteOrder order;
291 		ExifRational c1,c2,c3;
292 		gfloat f;
293 		gchar ref;
294 
295 		order = exif_data_get_byte_order (exif);
296 		c1 = exif_get_rational (entry->data, order);
297 		c2 = exif_get_rational (entry->data+8, order);
298 		c3 = exif_get_rational (entry->data+16, order);
299 		ref = exif_get_short (refentry->data, order);
300 
301 		/* Avoid ridiculous values */
302 		if (c1.denominator == 0 ||
303 		    c2.denominator == 0 ||
304 		    c3.denominator == 0) {
305 			return NULL;
306 		}
307 
308 		f = (double)c1.numerator/c1.denominator+
309 		    (double)c2.numerator/(c2.denominator*60)+
310 		    (double)c3.numerator/(c3.denominator*60*60);
311 
312 		if (ref == 'S' || ref == 'W') {
313 			f = -1 * f;
314 		}
315 
316 		return g_strdup_printf ("%f", f);
317 	}
318 
319 	return NULL;
320 }
321 
322 static gchar *
323 get_gps_altitude (ExifData *exif,
324                   ExifTag   tag,
325                   ExifTag   reftag)
326 {
327 	ExifEntry *entry = exif_data_get_entry (exif, tag);
328 	ExifEntry *refentry = exif_data_get_entry (exif, reftag);
329 
330 	if (entry) {
331 		ExifByteOrder order;
332 		ExifRational c;
333 		gfloat f;
334 
335 		order = exif_data_get_byte_order (exif);
336 		c = exif_get_rational (entry->data, order);
337 
338 		/* Avoid ridiculous values */
339 		if (c.denominator == 0) {
340 			return NULL;
341 		}
342 
343 		f = (double)c.numerator/c.denominator;
344 
345 		/* Strictly speaking it is invalid not to have this
346 		   but.. let's try to cope here */
347 		if (refentry) {
348 			ExifShort ref;
349 			ref = exif_get_short (refentry->data, order);
350 			if (ref == 1) {
351 				f = -1 * f;
352 			}
353 		}
354 		return g_strdup_printf ("%f", f);
355 	}
356 
357 	return NULL;
358 }
359 
360 static gint
361 get_int (ExifData *exif,
362          ExifTag   tag)
363 {
364 	ExifEntry *entry = exif_data_get_entry (exif, tag);
365 
366 	if (entry) {
367 		ExifByteOrder order;
368 
369 		order = exif_data_get_byte_order (exif);
370 		return (gint) exif_get_short (entry->data, order);
371 	}
372 
373 	return -1;
374 }
375 
376 
377 static gchar *
378 get_value (ExifData *exif,
379            ExifTag   tag)
380 {
381 	ExifEntry *entry = exif_data_get_entry (exif, tag);
382 
383 	if (entry) {
384 		gchar buf[1024];
385 
386 		exif_entry_get_value (entry, buf, 1024);
387 
388 		return g_strdup (buf);
389 	}
390 
391 	return NULL;
392 }
393 
394 #endif /* HAVE_LIBEXIF */
395 
396 static gboolean
397 parse_exif (const unsigned char *buffer,
398             size_t               len,
399             const gchar         *uri,
400             TrackerExifData     *data)
401 {
402 #ifdef HAVE_LIBEXIF
403 	ExifData *exif;
404 #endif
405 
406 	memset (data, 0, sizeof (TrackerExifData));
407 
408 #ifdef HAVE_LIBEXIF
409 
410 	exif = exif_data_new ();
411 
412 	g_return_val_if_fail (exif != NULL, FALSE);
413 
414 	exif_data_set_option (exif, EXIF_DATA_OPTION_IGNORE_UNKNOWN_TAGS);
415 	exif_data_unset_option (exif, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
416 	exif_data_set_option (exif, EXIF_DATA_OPTION_DONT_CHANGE_MAKER_NOTE);
417 
418 	exif_data_load_data (exif, (unsigned char *) buffer, len);
419 
420 	/* Unused in the current only user of this code (JPeg extractor)
421 	   if (!data->y_dimension)
422 	   data->y_dimension = get_value (exif, EXIF_TAG_PIXEL_Y_DIMENSION);
423 	   if (!data->x_dimension)
424 	   data->x_dimension = get_value (exif, EXIF_TAG_PIXEL_X_DIMENSION);
425 	   if (!data->image_width)
426 	   data->image_width = get_value (exif, EXIF_TAG_RELATED_IMAGE_WIDTH);
427 	*/
428 
429 	if (!data->document_name)
430 		data->document_name = get_value (exif, EXIF_TAG_DOCUMENT_NAME);
431 	if (!data->time)
432 		data->time = get_date (exif, EXIF_TAG_DATE_TIME);
433 	if (!data->time_original)
434 		data->time_original = get_date (exif, EXIF_TAG_DATE_TIME_ORIGINAL);
435 	if (!data->artist)
436 		data->artist = get_value (exif, EXIF_TAG_ARTIST);
437 	if (!data->user_comment)
438 		data->user_comment = get_value (exif, EXIF_TAG_USER_COMMENT);
439 	if (!data->description)
440 		data->description = get_value (exif, EXIF_TAG_IMAGE_DESCRIPTION);
441 	if (!data->make)
442 		data->make = get_value (exif, EXIF_TAG_MAKE);
443 	if (!data->model)
444 		data->model = get_value (exif, EXIF_TAG_MODEL);
445 	if (!data->orientation)
446 		data->orientation = get_orientation (exif, EXIF_TAG_ORIENTATION);
447 	if (!data->exposure_time)
448 		data->exposure_time = get_exposure_time (exif, EXIF_TAG_EXPOSURE_TIME);
449 	if (!data->fnumber)
450 		data->fnumber = get_fnumber (exif, EXIF_TAG_FNUMBER);
451 	if (!data->flash)
452 		data->flash = get_flash (exif, EXIF_TAG_FLASH);
453 	if (!data->focal_length)
454 		data->focal_length = get_focal_length (exif, EXIF_TAG_FOCAL_LENGTH);
455 	if (!data->iso_speed_ratings)
456 		data->iso_speed_ratings = get_value (exif, EXIF_TAG_ISO_SPEED_RATINGS);
457 	if (!data->metering_mode)
458 		data->metering_mode = get_metering_mode (exif, EXIF_TAG_METERING_MODE);
459 	if (!data->white_balance)
460 		data->white_balance = get_white_balance (exif, EXIF_TAG_WHITE_BALANCE);
461 	if (!data->copyright) {
462 		gchar *strip_off;
463 		data->copyright = get_value (exif, EXIF_TAG_COPYRIGHT);
464 		if (data->copyright) {
465 			/* Exif catenates this to the string, noticed the
466 			 * string change from below in libexif 0.6.20 */
467 			strip_off = strstr (data->copyright, " (Photographer) - [None] (Editor)");
468 			if (strip_off) {
469 				*strip_off = '\0';
470 			} else {
471 				/* Fall back to old string */
472 				strip_off = strstr (data->copyright, " (Photographer) -  (Editor)");
473 
474 				if (strip_off) {
475 					*strip_off = '\0';
476 				}
477 			}
478 		}
479 	}
480 	if (!data->software)
481 		data->software = get_value (exif, EXIF_TAG_SOFTWARE);
482 
483 	if (!data->resolution_unit)
484 		data->resolution_unit = get_int (exif, EXIF_TAG_RESOLUTION_UNIT);
485 	if (!data->x_resolution)
486 		data->x_resolution = get_value (exif, EXIF_TAG_X_RESOLUTION);
487 	if (!data->y_resolution)
488 		data->y_resolution = get_value (exif, EXIF_TAG_Y_RESOLUTION);
489 
490 	if(!data->gps_altitude)
491 		data->gps_altitude = get_gps_altitude (exif, EXIF_TAG_GPS_ALTITUDE, EXIF_TAG_GPS_ALTITUDE_REF);
492 	if(!data->gps_latitude)
493 		data->gps_latitude = get_gps_coordinate (exif, EXIF_TAG_GPS_LATITUDE, EXIF_TAG_GPS_LATITUDE_REF);
494 	if(!data->gps_longitude)
495 		data->gps_longitude = get_gps_coordinate (exif, EXIF_TAG_GPS_LONGITUDE, EXIF_TAG_GPS_LONGITUDE_REF);
496 	if(!data->gps_direction)
497 		data->gps_direction = get_value (exif, EXIF_TAG_GPS_IMG_DIRECTION);
498 
499 	exif_data_free (exif);
500 #endif /* HAVE_LIBEXIF */
501 
502 	return TRUE;
503 }
504 
505 #ifndef TRACKER_DISABLE_DEPRECATED
506 
507 /**
508  * tracker_exif_read:
509  * @buffer: a chunk of data with exif data in it.
510  * @len: the size of @buffer.
511  * @uri: the URI this is related to.
512  * @data: a pointer to a TrackerExifData struture to populate.
513  *
514  * This function takes @len bytes of @buffer and runs it through the
515  * EXIF library. The result is that @data is populated with the EXIF
516  * data found in @uri.
517  *
518  * Returns: %TRUE if the @data was populated successfully, otherwise
519  * %FALSE is returned.
520  *
521  * Since: 0.8
522  *
523  * Deprecated: 0.9. Use tracker_exif_new() instead.
524  **/
525 gboolean
526 tracker_exif_read (const unsigned char *buffer,
527                    size_t               len,
528                    const gchar         *uri,
529                    TrackerExifData     *data)
530 {
531 	g_return_val_if_fail (buffer != NULL, FALSE);
532 	g_return_val_if_fail (len > 0, FALSE);
533 	g_return_val_if_fail (uri != NULL, FALSE);
534 	g_return_val_if_fail (data != NULL, FALSE);
535 
536 	return parse_exif (buffer, len, uri, data);
537 }
538 
539 #endif /* TRACKER_DISABLE_DEPRECATED */
540 
541 /**
542  * tracker_exif_new:
543  * @buffer: a chunk of data with exif data in it.
544  * @len: the size of @buffer.
545  * @uri: the URI this is related to.
546  *
547  * This function takes @len bytes of @buffer and runs it through the
548  * EXIF library.
549  *
550  * Returns: a newly allocated #TrackerExifData struct if EXIF data was
551  * found, %NULL otherwise. Free the returned struct with tracker_exif_free().
552  *
553  * Since: 0.10
554  **/
555 TrackerExifData *
556 tracker_exif_new (const guchar *buffer,
557                   size_t        len,
558                   const gchar  *uri)
559 {
560 	TrackerExifData *data;
561 
562 	g_return_val_if_fail (buffer != NULL, NULL);
563 	g_return_val_if_fail (len > 0, NULL);
564 	g_return_val_if_fail (uri != NULL, NULL);
565 
566 	data = g_new0 (TrackerExifData, 1);
567 
568 	if (!parse_exif (buffer, len, uri, data)) {
569 		tracker_exif_free (data);
570 		return NULL;
571 	}
572 
573 	return data;
574 }
575 
576 /**
577  * tracker_exif_free:
578  * @data: a #TrackerExifData
579  *
580  * Frees @data and all #TrackerExifData members. %NULL will produce a
581  * a warning.
582  *
583  * Since: 0.10
584  **/
585 void
586 tracker_exif_free (TrackerExifData *data)
587 {
588 	g_return_if_fail (data != NULL);
589 
590 	g_free (data->y_dimension);
591 	g_free (data->x_dimension);
592 	g_free (data->image_width);
593 	g_free (data->document_name);
594 	g_free (data->time);
595 	g_free (data->time_original);
596 	g_free (data->artist);
597 	g_free (data->user_comment);
598 	g_free (data->description);
599 	g_free (data->make);
600 	g_free (data->model);
601 	g_free (data->orientation);
602 	g_free (data->exposure_time);
603 	g_free (data->fnumber);
604 	g_free (data->flash);
605 	g_free (data->focal_length);
606 	g_free (data->iso_speed_ratings);
607 	g_free (data->metering_mode);
608 	g_free (data->white_balance);
609 	g_free (data->copyright);
610 	g_free (data->software);
611 	g_free (data->x_resolution);
612 	g_free (data->y_resolution);
613 	g_free (data->gps_altitude);
614 	g_free (data->gps_latitude);
615 	g_free (data->gps_longitude);
616 	g_free (data->gps_direction);
617 
618 	g_free (data);
619 }