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 }