1 /* -*- Mode: CPP; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  GThumb
5  *
6  *  Copyright (C) 2008-2009 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 #include <config.h>
23 #include <glib.h>
24 #define GDK_PIXBUF_ENABLE_BACKEND
25 #include <gdk-pixbuf/gdk-pixbuf.h>
26 #include <exiv2/basicio.hpp>
27 #include <exiv2/error.hpp>
28 #include <exiv2/image.hpp>
29 #include <exiv2/exif.hpp>
30 #include <iostream>
31 #include <string>
32 #include <sstream>
33 #include <vector>
34 #include <iomanip>
35 #include <exiv2/exiv2.hpp>
36 #if EXIV2_TEST_VERSION(0, 27, 0)
37 #include <exiv2/xmp_exiv2.hpp>
38 #else
39 #include <exiv2/xmp.hpp>
40 #endif
41 #include <gthumb.h>
42 #include "exiv2-utils.h"
43 
44 using namespace std;
45 
46 
47 #define INVALID_VALUE N_("(invalid value)")
48 
49 
50 /* Some bits of information may be contained in more than one metadata tag.
51    The arrays below define the valid tags for a particular piece of
52    information, in decreasing order of preference (best one first) */
53 
54 const char *_DATE_TAG_NAMES[] = {
55 	"Exif::Image::DateTime",
56 	"Xmp::exif::DateTime",
57 	"Exif::Photo::DateTimeOriginal",
58 	"Xmp::exif::DateTimeOriginal",
59 	"Exif::Photo::DateTimeDigitized",
60 	"Xmp::exif::DateTimeDigitized",
61 	"Xmp::xmp::CreateDate",
62 	"Xmp::photoshop::DateCreated",
63 	"Xmp::xmp::ModifyDate",
64 	"Xmp::xmp::MetadataDate",
65 	NULL
66 };
67 
68 const char *_LAST_DATE_TAG_NAMES[] = {
69 	"Exif::Image::DateTime",
70 	"Xmp::exif::DateTime",
71 	"Xmp::xmp::ModifyDate",
72 	"Xmp::xmp::MetadataDate",
73 	NULL
74 };
75 
76 const char *_ORIGINAL_DATE_TAG_NAMES[] = {
77 	"Exif::Photo::DateTimeOriginal",
78 	"Xmp::exif::DateTimeOriginal",
79 	"Exif::Photo::DateTimeDigitized",
80 	"Xmp::exif::DateTimeDigitized",
81 	"Xmp::xmp::CreateDate",
82 	"Xmp::photoshop::DateCreated",
83 	NULL
84 };
85 
86 const char *_EXPOSURE_TIME_TAG_NAMES[] = {
87 	"Exif::Photo::ExposureTime",
88 	"Xmp::exif::ExposureTime",
89 	"Exif::Photo::ShutterSpeedValue",
90 	"Xmp::exif::ShutterSpeedValue",
91 	NULL
92 };
93 
94 const char *_EXPOSURE_MODE_TAG_NAMES[] = {
95 	"Exif::Photo::ExposureMode",
96 	"Xmp::exif::ExposureMode",
97 	NULL
98 };
99 
100 const char *_ISOSPEED_TAG_NAMES[] = {
101 	"Exif::Photo::ISOSpeedRatings",
102 	"Xmp::exif::ISOSpeedRatings",
103 	NULL
104 };
105 
106 const char *_APERTURE_TAG_NAMES[] = {
107 	"Exif::Photo::ApertureValue",
108 	"Xmp::exif::ApertureValue",
109 	"Exif::Photo::FNumber",
110 	"Xmp::exif::FNumber",
111 	NULL
112 };
113 
114 const char *_FOCAL_LENGTH_TAG_NAMES[] = {
115 	"Exif::Photo::FocalLength",
116 	"Xmp::exif::FocalLength",
117 	NULL
118 };
119 
120 const char *_SHUTTER_SPEED_TAG_NAMES[] = {
121 	"Exif::Photo::ShutterSpeedValue",
122 	"Xmp::exif::ShutterSpeedValue",
123 	NULL
124 };
125 
126 const char *_MAKE_TAG_NAMES[] = {
127 	"Exif::Image::Make",
128 	"Xmp::tiff::Make",
129 	NULL
130 };
131 
132 const char *_MODEL_TAG_NAMES[] = {
133 	"Exif::Image::Model",
134 	"Xmp::tiff::Model",
135 	NULL
136 };
137 
138 const char *_FLASH_TAG_NAMES[] = {
139 	"Exif::Photo::Flash",
140 	"Xmp::exif::Flash",
141 	NULL
142 };
143 
144 const char *_ORIENTATION_TAG_NAMES[] = {
145 	"Exif::Image::Orientation",
146 	"Xmp::tiff::Orientation",
147 	NULL
148 };
149 
150 const char *_DESCRIPTION_TAG_NAMES[] = {
151 	"Iptc::Application2::Caption",
152 	"Xmp::dc::description",
153 	"Exif::Photo::UserComment",
154 	"Exif::Image::ImageDescription",
155 	"Xmp::tiff::ImageDescription",
156 	"Iptc::Application2::Headline",
157 	NULL
158 };
159 
160 const char *_TITLE_TAG_NAMES[] = {
161 	"Xmp::dc::title",
162 	NULL
163 };
164 
165 const char *_LOCATION_TAG_NAMES[] = {
166 	"Xmp::iptc::Location",
167 	"Iptc::Application2::LocationName",
168 	NULL
169 };
170 
171 const char *_KEYWORDS_TAG_NAMES[] = {
172 	"Iptc::Application2::Keywords",
173 	"Xmp::iptc::Keywords",
174 	"Xmp::dc::subject",
175 	NULL
176 };
177 
178 const char *_RATING_TAG_NAMES[] = {
179 	"Xmp::xmp::Rating",
180 	NULL
181 };
182 
183 const char *_AUTHOR_TAG_NAMES[] = {
184 	"Exif::Image::Artist",
185 	"Iptc::Application2::Byline",
186 	"Xmp::dc::creator",
187 	"Xmp::xmpDM::artist",
188 	"Xmp::tiff::Artist",
189 	"Xmp::plus::ImageCreator",
190 	"Xmp::plus::ImageCreatorName",
191 	NULL
192 };
193 
194 const char *_COPYRIGHT_TAG_NAMES[] = {
195 	"Exif::Image::Copyright",
196 	"Iptc::Application2::Copyright",
197 	"Xmp::dc::rights",
198 	"Xmp::xmpDM::copyright",
199 	"Xmp::tiff::Copyright",
200 	"Xmp::plus::CopyrightOwner",
201 	"Xmp::plus::CopyrightOwnerName",
202 	NULL
203 };
204 
205 /* Some evil camera fill in the ImageDescription or UserComment fields
206    with useless fluff. Try to filter these out, so they do not show up
207    as comments */
208 const char *stupid_comment_filter[] = {
209 	"OLYMPUS DIGITAL CAMERA",
210 	"SONY DSC",
211 	"KONICA MINOLTA DIGITAL CAMERA",
212 	"MINOLTA DIGITAL CAMERA",
213 	NULL };
214 
215 
216 inline static char *
exiv2_key_from_attribute(const char * attribute)217 exiv2_key_from_attribute (const char *attribute)
218 {
219 	return _g_utf8_replace_str (attribute, "::", ".");
220 }
221 
222 
223 inline static char *
exiv2_key_to_attribute(const char * key)224 exiv2_key_to_attribute (const char *key)
225 {
226 	return _g_utf8_replace_str (key, ".", "::");
227 }
228 
229 
230 static gboolean
attribute_is_date(const char * key)231 attribute_is_date (const char *key)
232 {
233 	int i;
234 
235 	for (i = 0; _DATE_TAG_NAMES[i] != NULL; i++) {
236 		if (strcmp (_DATE_TAG_NAMES[i], key) == 0)
237 			return TRUE;
238 	}
239 
240 	return FALSE;
241 }
242 
243 
244 static GthMetadata *
create_metadata(const char * key,const char * description,const char * formatted_value,const char * raw_value,const char * category,const char * type_name)245 create_metadata (const char *key,
246 		 const char *description,
247 		 const char *formatted_value,
248 		 const char *raw_value,
249 		 const char *category,
250 		 const char *type_name)
251 {
252 	char            *formatted_value_utf8;
253 	char            *attribute;
254 	GthMetadataInfo *metadata_info;
255 	GthMetadata     *metadata;
256 	char            *description_utf8;
257 
258 	formatted_value_utf8 = _g_utf8_from_any (formatted_value);
259 	if (_g_utf8_all_spaces (formatted_value_utf8))
260 		return NULL;
261 
262 	description_utf8 = _g_utf8_from_any (description);
263 
264 	attribute = exiv2_key_to_attribute (key);
265 	if (attribute_is_date (attribute)) {
266 		GTimeVal time_;
267 
268 		g_free (formatted_value_utf8);
269 		formatted_value_utf8 = NULL;
270 
271 		if (_g_time_val_from_exif_date (raw_value, &time_))
272 			formatted_value_utf8 = _g_time_val_strftime (&time_, "%x %X");
273 		else
274 			formatted_value_utf8 = g_locale_to_utf8 (formatted_value, -1, NULL, NULL, NULL);
275 	}
276 	else if (_g_utf8_has_prefix (formatted_value_utf8, "lang=")) {
277 		const char *after_space;
278 		char       *formatted_clean;
279 
280 		after_space = _g_utf8_after_ascii_space (formatted_value_utf8);
281 		formatted_clean = g_strdup (after_space);
282 		g_free (formatted_value_utf8);
283 		formatted_value_utf8 = formatted_clean;
284 	}
285 
286 	if (formatted_value_utf8 == NULL)
287 		formatted_value_utf8 = g_strdup (INVALID_VALUE);
288 
289 	metadata_info = gth_main_get_metadata_info (attribute);
290 	if ((metadata_info == NULL) && (category != NULL)) {
291 		GthMetadataInfo info;
292 
293 		info.id = attribute;
294 		info.type = (type_name != NULL) ? g_strdup (type_name) : NULL;
295 		info.display_name = description_utf8;
296 		info.category = category;
297 		info.sort_order = 500;
298 		info.flags = GTH_METADATA_ALLOW_IN_PROPERTIES_VIEW;
299 		metadata_info = gth_main_register_metadata_info (&info);
300 	}
301 
302 	if ((metadata_info != NULL) && (metadata_info->type == NULL) && (type_name != NULL))
303 		metadata_info->type = g_strdup (type_name);
304 
305 	if ((metadata_info != NULL) && (metadata_info->display_name == NULL) && (description_utf8 != NULL))
306 		metadata_info->display_name = g_strdup (description_utf8);
307 
308 	metadata = gth_metadata_new ();
309 	g_object_set (metadata,
310 		      "id", key,
311 		      "description", description_utf8,
312 		      "formatted", formatted_value_utf8,
313 		      "raw", raw_value,
314 		      "value-type", type_name,
315 		      NULL);
316 
317 	g_free (formatted_value_utf8);
318 	g_free (description_utf8);
319 	g_free (attribute);
320 
321 	return metadata;
322 }
323 
324 
325 static void
add_string_list_to_metadata(GthMetadata * metadata,const Exiv2::Metadatum & value)326 add_string_list_to_metadata (GthMetadata            *metadata,
327 			     const Exiv2::Metadatum &value)
328 {
329 	GList         *list;
330 	GthStringList *string_list;
331 
332 	list = NULL;
333 	for (int i = 0; i < value.count(); i++)
334 		list = g_list_prepend (list, g_strdup (value.toString(i).c_str()));
335 	string_list = gth_string_list_new (g_list_reverse (list));
336 	g_object_set (metadata, "string-list", string_list, NULL);
337 
338 	g_object_unref (string_list);
339 	_g_string_list_free (list);
340 }
341 
342 
343 static void
set_file_info(GFileInfo * info,const char * key,const char * description,const char * formatted_value,const char * raw_value,const char * category,const char * type_name)344 set_file_info (GFileInfo  *info,
345 	       const char *key,
346 	       const char *description,
347 	       const char *formatted_value,
348 	       const char *raw_value,
349 	       const char *category,
350 	       const char *type_name)
351 {
352 	char        *attribute;
353 	GthMetadata *metadata;
354 
355 	attribute = exiv2_key_to_attribute (key);
356 	metadata = create_metadata (key, description, formatted_value, raw_value, category, type_name);
357 	if (metadata != NULL) {
358 		g_file_info_set_attribute_object (info, attribute, G_OBJECT (metadata));
359 		g_object_unref (metadata);
360 	}
361 
362 	g_free (attribute);
363 }
364 
365 
366 GHashTable *
create_metadata_hash(void)367 create_metadata_hash (void)
368 {
369 	return g_hash_table_new_full (g_str_hash,
370 				      g_str_equal,
371 				      g_free,
372 				      g_object_unref);
373 }
374 
375 
376 static void
add_metadata_to_hash(GHashTable * table,GthMetadata * metadata)377 add_metadata_to_hash (GHashTable  *table,
378 		      GthMetadata *metadata)
379 {
380 	char     *key;
381 	gpointer  object;
382 
383 	if (metadata == NULL)
384 		return;
385 
386 	key = exiv2_key_to_attribute (gth_metadata_get_id (metadata));
387 	object = g_hash_table_lookup (table, key);
388 	if (object != NULL) {
389 		GthStringList *string_list;
390 		GList         *list;
391 
392 		string_list = NULL;
393 		switch (gth_metadata_get_data_type (GTH_METADATA (object))) {
394 		case GTH_METADATA_TYPE_STRING:
395 			string_list = gth_string_list_new (NULL);
396 			list = g_list_append (NULL, g_strdup (gth_metadata_get_formatted (GTH_METADATA (object))));
397 			gth_string_list_set_list (string_list, list);
398 			break;
399 
400 		case GTH_METADATA_TYPE_STRING_LIST:
401 			string_list = (GthStringList *) g_object_ref (gth_metadata_get_string_list (GTH_METADATA (object)));
402 			break;
403 		}
404 
405 		if (string_list == NULL) {
406 			g_hash_table_insert (table,
407 					     g_strdup (key),
408 					     g_object_ref (metadata));
409 			return;
410 		}
411 
412 		switch (gth_metadata_get_data_type (metadata)) {
413 		case GTH_METADATA_TYPE_STRING:
414 			list = gth_string_list_get_list (string_list);
415 			list = g_list_append (list, g_strdup (gth_metadata_get_formatted (metadata)));
416 			gth_string_list_set_list (string_list, list);
417 			break;
418 
419 		case GTH_METADATA_TYPE_STRING_LIST:
420 			gth_string_list_concat (string_list, gth_metadata_get_string_list (metadata));
421 			break;
422 		}
423 
424 		g_object_set (metadata, "string-list", string_list, NULL);
425 		g_hash_table_replace (table,
426 				      g_strdup (key),
427 				      g_object_ref (metadata));
428 
429 		g_object_unref (string_list);
430 	}
431 	else
432 		g_hash_table_insert (table,
433 				     g_strdup (key),
434 				     g_object_ref (metadata));
435 
436 	g_free (key);
437 }
438 
439 
440 static void
set_file_info_from_hash(GFileInfo * info,GHashTable * table)441 set_file_info_from_hash (GFileInfo  *info,
442 			 GHashTable *table)
443 {
444 	GHashTableIter iter;
445 	gpointer       key;
446 	gpointer       value;
447 
448 	g_hash_table_iter_init (&iter, table);
449 	while (g_hash_table_iter_next (&iter, &key, &value))
450 		g_file_info_set_attribute_object (info, (char *)key, G_OBJECT (value));
451 }
452 
453 
454 static void
set_attribute_from_metadata(GFileInfo * info,const char * attribute,GObject * metadata)455 set_attribute_from_metadata (GFileInfo  *info,
456 			     const char *attribute,
457 			     GObject    *metadata)
458 {
459 	char *description;
460 	char *formatted_value;
461 	char *raw_value;
462 	char *type_name;
463 
464 	if (metadata == NULL)
465 		return;
466 
467 	g_object_get (metadata,
468 		      "description", &description,
469 		      "formatted", &formatted_value,
470 		      "raw", &raw_value,
471 		      "value-type", &type_name,
472 		      NULL);
473 
474 	set_file_info (info,
475 		       attribute,
476 		       description,
477 		       formatted_value,
478 		       raw_value,
479 		       NULL,
480 		       type_name);
481 
482 	g_free (description);
483 	g_free (formatted_value);
484 	g_free (raw_value);
485 	g_free (type_name);
486 }
487 
488 
489 static GObject *
get_attribute_from_tagset(GFileInfo * info,const char * tagset[])490 get_attribute_from_tagset (GFileInfo  *info,
491 			   const char *tagset[])
492 {
493 	GObject *metadata;
494 	int      i;
495 
496 	metadata = NULL;
497 	for (i = 0; tagset[i] != NULL; i++) {
498 		metadata = g_file_info_get_attribute_object (info, tagset[i]);
499 		if (metadata != NULL)
500 			return metadata;
501 	}
502 
503 	return NULL;
504 }
505 
506 
507 static void
set_attribute_from_tagset(GFileInfo * info,const char * attribute,const char * tagset[])508 set_attribute_from_tagset (GFileInfo  *info,
509 			   const char *attribute,
510 			   const char *tagset[])
511 {
512 	GObject *metadata;
513 
514 	metadata = get_attribute_from_tagset (info, tagset);
515 	if (metadata != NULL)
516 		set_attribute_from_metadata (info, attribute, metadata);
517 }
518 
519 
520 static void
set_string_list_attribute_from_tagset(GFileInfo * info,const char * attribute,const char * tagset[])521 set_string_list_attribute_from_tagset (GFileInfo  *info,
522 				       const char *attribute,
523 				       const char *tagset[])
524 {
525 	GObject *metadata;
526 	int      i;
527 
528 	metadata = NULL;
529 	for (i = 0; tagset[i] != NULL; i++) {
530 		metadata = g_file_info_get_attribute_object (info, tagset[i]);
531 		if (metadata != NULL)
532 			break;
533 	}
534 
535 	if (metadata == NULL)
536 		return;
537 
538 	if (GTH_IS_METADATA (metadata) && (gth_metadata_get_data_type (GTH_METADATA (metadata)) != GTH_METADATA_TYPE_STRING_LIST)) {
539 		char           *raw;
540 		char           *utf8_raw;
541 		char          **keywords;
542 		GthStringList  *string_list;
543 
544 		g_object_get (metadata, "raw", &raw, NULL);
545 		utf8_raw = _g_utf8_try_from_any (raw);
546 		if (utf8_raw == NULL)
547 			return;
548 
549 		keywords = g_strsplit (utf8_raw, ",", -1);
550 		string_list = gth_string_list_new_from_strv (keywords);
551 		metadata = (GObject *) gth_metadata_new_for_string_list (string_list);
552 		g_file_info_set_attribute_object (info, attribute, metadata);
553 
554 		g_object_unref (metadata);
555 		g_object_unref (string_list);
556 		g_strfreev (keywords);
557 		g_free (raw);
558 		g_free (utf8_raw);
559 	}
560 	else
561 		g_file_info_set_attribute_object (info, attribute, metadata);
562 }
563 
564 
565 static void
clear_studip_comments_from_tagset(GFileInfo * info,const char * tagset[])566 clear_studip_comments_from_tagset (GFileInfo  *info,
567 				   const char *tagset[])
568 {
569 	int i;
570 
571 	for (i = 0; tagset[i] != NULL; i++) {
572 		GObject    *metadata;
573 		const char *value;
574 		int         j;
575 
576 		metadata = g_file_info_get_attribute_object (info, tagset[i]);
577 		if ((metadata == NULL) || ! GTH_IS_METADATA (metadata))
578 			continue;
579 
580 		value = gth_metadata_get_formatted (GTH_METADATA (metadata));
581 		for (j = 0; stupid_comment_filter[j] != NULL; j++) {
582 			if (strstr (value, stupid_comment_filter[j]) == value) {
583 				g_file_info_remove_attribute (info, tagset[i]);
584 				break;
585 			}
586 		}
587 	}
588 }
589 
590 
591 extern "C"
592 void
exiv2_update_general_attributes(GFileInfo * info)593 exiv2_update_general_attributes (GFileInfo *info)
594 {
595 	set_attribute_from_tagset (info, "general::datetime", _ORIGINAL_DATE_TAG_NAMES);
596 	set_attribute_from_tagset (info, "general::description", _DESCRIPTION_TAG_NAMES);
597 	set_attribute_from_tagset (info, "general::title", _TITLE_TAG_NAMES);
598 
599 	/* if iptc::caption and iptc::headline are different use iptc::headline
600 	 * to set general::title, if not already set. */
601 
602 	if (g_file_info_get_attribute_object (info, "general::title") == NULL) {
603 		GObject *iptc_caption;
604 		GObject *iptc_headline;
605 
606 		iptc_caption = g_file_info_get_attribute_object (info, "Iptc::Application2::Caption");
607 		iptc_headline = g_file_info_get_attribute_object (info, "Iptc::Application2::Headline");
608 
609 		if ((iptc_caption != NULL)
610 		    && (iptc_headline != NULL)
611 		    && (g_strcmp0 (gth_metadata_get_raw (GTH_METADATA (iptc_caption)),
612 				   gth_metadata_get_raw (GTH_METADATA (iptc_headline))) != 0))
613 		{
614 			set_attribute_from_metadata (info, "general::title", iptc_headline);
615 		}
616 	}
617 
618 	set_attribute_from_tagset (info, "general::location", _LOCATION_TAG_NAMES);
619 	set_string_list_attribute_from_tagset (info, "general::tags", _KEYWORDS_TAG_NAMES);
620 	set_attribute_from_tagset (info, "general::rating", _RATING_TAG_NAMES);
621 }
622 
623 
624 #define EXPOSURE_SEPARATOR " · "
625 
626 
627 static void
set_attributes_from_tagsets(GFileInfo * info,gboolean update_general_attributes)628 set_attributes_from_tagsets (GFileInfo *info,
629 			     gboolean   update_general_attributes)
630 {
631 	clear_studip_comments_from_tagset (info, _DESCRIPTION_TAG_NAMES);
632 	clear_studip_comments_from_tagset (info, _TITLE_TAG_NAMES);
633 
634 	if (update_general_attributes)
635 		exiv2_update_general_attributes (info);
636 
637 	set_attribute_from_tagset (info, "Embedded::Photo::DateTimeOriginal", _ORIGINAL_DATE_TAG_NAMES);
638 	set_attribute_from_tagset (info, "Embedded::Image::Orientation", _ORIENTATION_TAG_NAMES);
639 
640 	set_attribute_from_tagset (info, "Embedded::Photo::Aperture", _APERTURE_TAG_NAMES);
641 	set_attribute_from_tagset (info, "Embedded::Photo::ISOSpeed", _ISOSPEED_TAG_NAMES);
642 	set_attribute_from_tagset (info, "Embedded::Photo::ExposureTime", _EXPOSURE_TIME_TAG_NAMES);
643 	set_attribute_from_tagset (info, "Embedded::Photo::ShutterSpeed", _SHUTTER_SPEED_TAG_NAMES);
644 	set_attribute_from_tagset (info, "Embedded::Photo::FocalLength", _FOCAL_LENGTH_TAG_NAMES);
645 	set_attribute_from_tagset (info, "Embedded::Photo::Flash", _FLASH_TAG_NAMES);
646 	set_attribute_from_tagset (info, "Embedded::Photo::CameraModel", _MODEL_TAG_NAMES);
647 	set_attribute_from_tagset (info, "Embedded::Photo::Author", _AUTHOR_TAG_NAMES);
648 	set_attribute_from_tagset (info, "Embedded::Photo::Copyright", _COPYRIGHT_TAG_NAMES);
649 
650 	/* Embedded::Photo::Exposure */
651 
652 	GObject *aperture;
653 	GObject *iso_speed;
654 	GObject *shutter_speed;
655 	GObject *exposure_time;
656 	GString *exposure;
657 
658 	aperture = get_attribute_from_tagset (info, _APERTURE_TAG_NAMES);
659 	iso_speed = get_attribute_from_tagset (info, _ISOSPEED_TAG_NAMES);
660 	shutter_speed = get_attribute_from_tagset (info, _SHUTTER_SPEED_TAG_NAMES);
661 	exposure_time = get_attribute_from_tagset (info, _EXPOSURE_TIME_TAG_NAMES);
662 
663 	exposure = g_string_new ("");
664 
665 	if (aperture != NULL) {
666 		char *formatted_value;
667 
668 		g_object_get (aperture, "formatted", &formatted_value, NULL);
669 		if (formatted_value != NULL) {
670 			g_string_append (exposure, formatted_value);
671 			g_free (formatted_value);
672 		}
673 	}
674 
675 	if (iso_speed != NULL) {
676 		char *formatted_value;
677 
678 		g_object_get (iso_speed, "formatted", &formatted_value, NULL);
679 		if (formatted_value != NULL) {
680 			if (exposure->len > 0)
681 				g_string_append (exposure, EXPOSURE_SEPARATOR);
682 			g_string_append (exposure, "ISO ");
683 			g_string_append (exposure, formatted_value);
684 			g_free (formatted_value);
685 		}
686 	}
687 
688 	if (shutter_speed != NULL) {
689 		char *formatted_value;
690 
691 		g_object_get (shutter_speed, "formatted", &formatted_value, NULL);
692 		if (formatted_value != NULL) {
693 			if (exposure->len > 0)
694 				g_string_append (exposure, EXPOSURE_SEPARATOR);
695 			g_string_append (exposure, formatted_value);
696 			g_free (formatted_value);
697 		}
698 	}
699 	else if (exposure_time != NULL) {
700 		char *formatted_value;
701 
702 		g_object_get (exposure_time, "formatted", &formatted_value, NULL);
703 		if (formatted_value != NULL) {
704 			if (exposure->len > 0)
705 				g_string_append (exposure, EXPOSURE_SEPARATOR);
706 			g_string_append (exposure, formatted_value);
707 			g_free (formatted_value);
708 		}
709 	}
710 
711 	set_file_info (info,
712 		       "Embedded::Photo::Exposure",
713 		       _("Exposure"),
714 		       exposure->str,
715 		       NULL,
716 		       NULL,
717 		       NULL);
718 
719 	g_string_free (exposure, TRUE);
720 }
721 
722 
723 static const char *
get_exif_default_category(const Exiv2::Exifdatum & md)724 get_exif_default_category (const Exiv2::Exifdatum &md)
725 {
726 #if EXIV2_TEST_VERSION(0, 21, 0)
727 	if (Exiv2::ExifTags::isMakerGroup(md.groupName()))
728 #else
729 	if (Exiv2::ExifTags::isMakerIfd(md.ifdId()))
730 #endif
731 		return "Exif::MakerNotes";
732 
733 	if (md.groupName().compare("Thumbnail") == 0)
734 		return "Exif::Thumbnail";
735 	else if (md.groupName().compare("GPSInfo") == 0)
736 		return "Exif::GPS";
737 	else if (md.groupName().compare("Iop") == 0)
738 		return "Exif::Versions";
739 
740 	return "Exif::Other";
741 }
742 
743 
744 static void
exiv2_read_metadata(Exiv2::Image::AutoPtr image,GFileInfo * info,gboolean update_general_attributes)745 exiv2_read_metadata (Exiv2::Image::AutoPtr  image,
746 		     GFileInfo             *info,
747 		     gboolean               update_general_attributes)
748 {
749 	image->readMetadata();
750 
751 	Exiv2::ExifData &exifData = image->exifData();
752 	if (! exifData.empty()) {
753 		Exiv2::ExifData::const_iterator end = exifData.end();
754 		for (Exiv2::ExifData::const_iterator md = exifData.begin(); md != end; ++md) {
755 			stringstream raw_value;
756 			raw_value << md->value();
757 
758 			stringstream description;
759 			if (! md->tagLabel().empty())
760 				description << md->tagLabel();
761 #if EXIV2_TEST_VERSION(0, 21, 0)
762 			else if (Exiv2::ExifTags::isMakerGroup(md->groupName()))
763 #else
764 			else if (Exiv2::ExifTags::isMakerIfd(md->ifdId()))
765 #endif
766 				// Must be a MakerNote - include group name
767 				description << md->groupName() << "." << md->tagName();
768 			else
769 				// Normal exif tag - just use tag name
770 				description << md->tagName();
771 
772 			set_file_info (info,
773 				       md->key().c_str(),
774 				       description.str().c_str(),
775 				       md->print(&exifData).c_str(),
776 				       raw_value.str().c_str(),
777 				       get_exif_default_category (*md),
778 				       md->typeName());
779 		}
780 	}
781 
782 	Exiv2::IptcData &iptcData = image->iptcData();
783 	if (! iptcData.empty()) {
784 		GHashTable *table = create_metadata_hash ();
785 
786 		Exiv2::IptcData::iterator end = iptcData.end();
787 		for (Exiv2::IptcData::iterator md = iptcData.begin(); md != end; ++md) {
788 			stringstream raw_value;
789 			raw_value << md->value();
790 
791 			stringstream description;
792 			if (! md->tagLabel().empty())
793 				description << md->tagLabel();
794 			else
795 				description << md->tagName();
796 
797 			GthMetadata *metadata;
798 			metadata = create_metadata (md->key().c_str(),
799 						    description.str().c_str(),
800 						    md->print().c_str(),
801 						    raw_value.str().c_str(),
802 						    "Iptc",
803 						    md->typeName());
804 			if (metadata != NULL) {
805 				add_metadata_to_hash (table, metadata);
806 				g_object_unref (metadata);
807 			}
808 		}
809 
810 		set_file_info_from_hash (info, table);
811 		g_hash_table_unref (table);
812 	}
813 
814 	Exiv2::XmpData &xmpData = image->xmpData();
815 	if (! xmpData.empty()) {
816 		GHashTable *table = create_metadata_hash ();
817 
818 		Exiv2::XmpData::iterator end = xmpData.end();
819 		for (Exiv2::XmpData::iterator md = xmpData.begin(); md != end; ++md) {
820 			stringstream raw_value;
821 			raw_value << md->value();
822 
823 			stringstream description;
824 			if (! md->tagLabel().empty())
825 				description << md->tagLabel();
826 			else
827 				description << md->groupName() << "." << md->tagName();
828 
829 			GthMetadata *metadata;
830 			metadata = create_metadata (md->key().c_str(),
831 						    description.str().c_str(),
832 						    md->print().c_str(),
833 						    raw_value.str().c_str(),
834 						    "Xmp::Embedded",
835 						    md->typeName());
836 			if (metadata != NULL) {
837 				if ((g_strcmp0 (md->typeName(), "XmpBag") == 0)
838 				    || (g_strcmp0 (md->typeName(), "XmpSeq") == 0))
839 				{
840 					add_string_list_to_metadata (metadata, *md);
841 				}
842 
843 				add_metadata_to_hash (table, metadata);
844 				g_object_unref (metadata);
845 			}
846 		}
847 
848 		set_file_info_from_hash (info, table);
849 		g_hash_table_unref (table);
850 	}
851 
852 	set_attributes_from_tagsets (info, update_general_attributes);
853 }
854 
855 
856 /*
857  * exiv2_read_metadata_from_file
858  * reads metadata from image files
859  * code relies heavily on example1 from the exiv2 website
860  * http://www.exiv2.org/example1.html
861  */
862 extern "C"
863 gboolean
exiv2_read_metadata_from_file(GFile * file,GFileInfo * info,gboolean update_general_attributes,GCancellable * cancellable,GError ** error)864 exiv2_read_metadata_from_file (GFile         *file,
865 			       GFileInfo     *info,
866 			       gboolean       update_general_attributes,
867 			       GCancellable  *cancellable,
868 			       GError       **error)
869 {
870 	try {
871 		char *path;
872 
873 		path = g_file_get_path (file);
874 		if (path == NULL) {
875 			if (error != NULL)
876 				*error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, _("Invalid file format"));
877 			return FALSE;
878 		}
879 
880 		Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path);
881 		g_free (path);
882 
883 		if (image.get() == 0) {
884 			if (error != NULL)
885 				*error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, _("Invalid file format"));
886 			return FALSE;
887 		}
888 		// Set the log level to only show errors (and suppress warnings, informational and debug messages)
889 		Exiv2::LogMsg::setLevel(Exiv2::LogMsg::error);
890 		exiv2_read_metadata (image, info, update_general_attributes);
891 	}
892 	catch (Exiv2::AnyError& e) {
893 		if (error != NULL)
894 			*error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, e.what());
895 		return FALSE;
896 	}
897 
898 	return TRUE;
899 }
900 
901 
902 extern "C"
903 gboolean
exiv2_read_metadata_from_buffer(void * buffer,gsize buffer_size,GFileInfo * info,gboolean update_general_attributes,GError ** error)904 exiv2_read_metadata_from_buffer (void       *buffer,
905 				 gsize       buffer_size,
906 				 GFileInfo  *info,
907 				 gboolean    update_general_attributes,
908 				 GError    **error)
909 {
910 	try {
911 		Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open ((Exiv2::byte*) buffer, buffer_size);
912 
913 		if (image.get() == 0) {
914 			if (error != NULL)
915 				*error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, _("Invalid file format"));
916 			return FALSE;
917 		}
918 
919 		exiv2_read_metadata (image, info, update_general_attributes);
920 	}
921 	catch (Exiv2::AnyError& e) {
922 		if (error != NULL)
923 			*error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, e.what());
924 		return FALSE;
925 	}
926 
927 	return TRUE;
928 }
929 
930 
931 extern "C"
932 GFile *
exiv2_get_sidecar(GFile * file)933 exiv2_get_sidecar (GFile *file)
934 {
935 	char  *uri;
936 	char  *uri_wo_ext;
937 	char  *sidecar_uri;
938 	GFile *sidecar;
939 
940 	uri = g_file_get_uri (file);
941 	uri_wo_ext = _g_uri_remove_extension (uri);
942 	sidecar_uri = g_strconcat (uri_wo_ext, ".xmp", NULL);
943 	sidecar = g_file_new_for_uri (sidecar_uri);
944 
945 	g_free (sidecar_uri);
946 	g_free (uri_wo_ext);
947 	g_free (uri);
948 
949 	return sidecar;
950 }
951 
952 
953 extern "C"
954 gboolean
exiv2_read_sidecar(GFile * file,GFileInfo * info,gboolean update_general_attributes)955 exiv2_read_sidecar (GFile     *file,
956 		    GFileInfo *info,
957 		    gboolean   update_general_attributes)
958 {
959 	try {
960 		char *path;
961 
962 		path = g_file_get_path (file);
963 		if (path == NULL)
964 			return FALSE;
965 
966 		Exiv2::DataBuf buf = Exiv2::readFile(path);
967 		g_free (path);
968 
969 		std::string xmpPacket;
970 		xmpPacket.assign(reinterpret_cast<char*>(buf.pData_), buf.size_);
971 		Exiv2::XmpData xmpData;
972 
973 		if (0 != Exiv2::XmpParser::decode(xmpData, xmpPacket))
974 			return FALSE;
975 
976 		if (! xmpData.empty()) {
977 			GHashTable *table = create_metadata_hash ();
978 
979 			Exiv2::XmpData::iterator end = xmpData.end();
980 			for (Exiv2::XmpData::iterator md = xmpData.begin(); md != end; ++md) {
981 				stringstream raw_value;
982 				raw_value << md->value();
983 
984 				stringstream description;
985 				if (! md->tagLabel().empty())
986 					description << md->tagLabel();
987 				else
988 					description << md->groupName() << "." << md->tagName();
989 
990 				GthMetadata *metadata;
991 				metadata = create_metadata (md->key().c_str(),
992 							    description.str().c_str(),
993 							    md->print().c_str(),
994 							    raw_value.str().c_str(),
995 							    "Xmp::Sidecar",
996 							    md->typeName());
997 				if (metadata != NULL) {
998 					if ((g_strcmp0 (md->typeName(), "XmpBag") == 0)
999 					    || (g_strcmp0 (md->typeName(), "XmpSeq") == 0))
1000 					{
1001 						add_string_list_to_metadata (metadata, *md);
1002 					}
1003 
1004 					add_metadata_to_hash (table, metadata);
1005 					g_object_unref (metadata);
1006 				}
1007 			}
1008 
1009 			set_file_info_from_hash (info, table);
1010 			g_hash_table_unref (table);
1011 		}
1012 		Exiv2::XmpParser::terminate();
1013 
1014 		set_attributes_from_tagsets (info, update_general_attributes);
1015 	}
1016 	catch (Exiv2::AnyError& e) {
1017 		std::cerr << "Caught Exiv2 exception '" << e << "'\n";
1018 		return FALSE;
1019 	}
1020 
1021 	return TRUE;
1022 }
1023 
1024 
1025 static void
mandatory_int(Exiv2::ExifData & checkdata,const char * tag,int value)1026 mandatory_int (Exiv2::ExifData &checkdata,
1027 	       const char      *tag,
1028 	       int              value)
1029 {
1030 	Exiv2::ExifKey key = Exiv2::ExifKey(tag);
1031 	if (checkdata.findKey(key) == checkdata.end())
1032 		checkdata[tag] = value;
1033 }
1034 
1035 
1036 static void
mandatory_string(Exiv2::ExifData & checkdata,const char * tag,const char * value)1037 mandatory_string (Exiv2::ExifData &checkdata,
1038 		  const char      *tag,
1039 		  const char      *value)
1040 {
1041 	Exiv2::ExifKey key = Exiv2::ExifKey(tag);
1042 	if (checkdata.findKey(key) == checkdata.end())
1043 		checkdata[tag] = value;
1044 }
1045 
1046 
1047 const char *
gth_main_get_metadata_type(gpointer metadata,const char * attribute)1048 gth_main_get_metadata_type (gpointer    metadata,
1049 			    const char *attribute)
1050 {
1051 	const char      *value_type = NULL;
1052 	GthMetadataInfo *metadatum_info;
1053 
1054 	if (GTH_IS_METADATA (metadata)) {
1055 		value_type = gth_metadata_get_value_type (GTH_METADATA (metadata));
1056 		if ((g_strcmp0 (value_type, "Undefined") == 0) || (g_strcmp0 (value_type, "") == 0))
1057 			value_type = NULL;
1058 
1059 		if (value_type != NULL)
1060 			return value_type;
1061 	}
1062 
1063 	metadatum_info = gth_main_get_metadata_info (attribute);
1064 	if (metadatum_info != NULL)
1065 		value_type = metadatum_info->type;
1066 
1067 	return value_type;
1068 }
1069 
1070 
1071 static void
dump_exif_data(Exiv2::ExifData & exifData,const char * prefix)1072 dump_exif_data (Exiv2::ExifData &exifData,
1073 		const char      *prefix)
1074 {
1075 	std::cout << prefix << "\n";
1076 
1077 	try {
1078 		if (exifData.empty()) {
1079 #if EXIV2_TEST_VERSION(0, 27, 0)
1080 			throw Exiv2::Error(Exiv2::kerErrorMessage, " No Exif data found in the file");
1081 #else
1082 			throw Exiv2::Error(1, " No Exif data found in the file");
1083 #endif
1084 		}
1085 		Exiv2::ExifData::const_iterator end = exifData.end();
1086 		for (Exiv2::ExifData::const_iterator i = exifData.begin(); i != end; ++i) {
1087 			const char* tn = i->typeName();
1088 			std::cout << std::setw(44) << std::setfill(' ') << std::left
1089 				  << i->key() << " "
1090 				  << "0x" << std::setw(4) << std::setfill('0') << std::right
1091 				  << std::hex << i->tag() << " "
1092 				  << std::setw(9) << std::setfill(' ') << std::left
1093 				  << (tn ? tn : "Unknown") << " "
1094 				  << std::dec << std::setw(3)
1095 				  << std::setfill(' ') << std::right
1096 				  << i->count() << "  "
1097 				  << std::dec << i->value()
1098 				  << "\n";
1099 		}
1100 		std::cout << "\n";
1101 	}
1102 	catch (Exiv2::Error& e) {
1103 	    std::cout << "Caught Exiv2 exception '" << e.what() << "'\n";
1104 	    return;
1105 	}
1106 }
1107 
1108 
1109 static Exiv2::DataBuf
exiv2_write_metadata_private(Exiv2::Image::AutoPtr image,GFileInfo * info,GthImage * image_data)1110 exiv2_write_metadata_private (Exiv2::Image::AutoPtr  image,
1111 			      GFileInfo             *info,
1112 			      GthImage              *image_data)
1113 {
1114 	static char  *software_name = NULL;
1115 	char        **attributes;
1116 	int           i;
1117 
1118 	image->clearMetadata();
1119 
1120 	// EXIF Data
1121 
1122 	Exiv2::ExifData ed;
1123 	attributes = g_file_info_list_attributes (info, "Exif");
1124 	for (i = 0; attributes[i] != NULL; i++) {
1125 		GthMetadata *metadatum;
1126 		char *key;
1127 
1128 		metadatum = (GthMetadata *) g_file_info_get_attribute_object (info, attributes[i]);
1129 		key = exiv2_key_from_attribute (attributes[i]);
1130 
1131 		try {
1132 			/* If the metadatum has no value yet, a new empty value
1133 			 * is created. The type is taken from Exiv2's tag
1134 			 * lookup tables. If the tag is not found in the table,
1135 			 * the type defaults to ASCII.
1136 			 * We always create the metadatum explicitly if the
1137 			 * type is available to avoid type errors.
1138 			 * See bug #610389 for more details.  The original
1139 			 * explanation is here:
1140 			 * http://uk.groups.yahoo.com/group/exiv2/message/1472
1141 			 */
1142 
1143 			const char *raw_value = gth_metadata_get_raw (metadatum);
1144 			const char *value_type = gth_main_get_metadata_type (metadatum, attributes[i]);
1145 
1146 			if ((raw_value != NULL) && (strcmp (raw_value, "") != 0) && (value_type != NULL)) {
1147 				Exiv2::Value::AutoPtr value = Exiv2::Value::create (Exiv2::TypeInfo::typeId (value_type));
1148 				value->read (raw_value);
1149 				Exiv2::ExifKey exif_key(key);
1150 				ed.add (exif_key, value.get());
1151 			}
1152 		}
1153 		catch (Exiv2::AnyError& e) {
1154 			/* we don't care about invalid key errors */
1155 			g_warning ("%s", e.what());
1156 		}
1157 
1158 		g_free (key);
1159 	}
1160 	g_strfreev (attributes);
1161 
1162 	// Mandatory tags - add if not already present
1163 
1164 	mandatory_int (ed, "Exif.Image.XResolution", 72);
1165 	mandatory_int (ed, "Exif.Image.YResolution", 72);
1166 	mandatory_int (ed, "Exif.Image.ResolutionUnit", 2);
1167 	mandatory_int (ed, "Exif.Image.YCbCrPositioning", 1);
1168 	mandatory_int (ed, "Exif.Photo.ColorSpace", 1);
1169 	mandatory_string (ed, "Exif.Photo.ExifVersion", "48 50 50 49");
1170 	mandatory_string (ed, "Exif.Photo.ComponentsConfiguration", "1 2 3 0");
1171 	mandatory_string (ed, "Exif.Photo.FlashpixVersion", "48 49 48 48");
1172 
1173 	// Overwrite the software tag if the image content was modified
1174 
1175 	if (g_file_info_get_attribute_boolean (info, "gth::file::image-changed")) {
1176 		if (software_name == NULL)
1177 			software_name = g_strconcat (g_get_application_name (), " ", PACKAGE_VERSION, NULL);
1178 		ed["Exif.Image.ProcessingSoftware"] = software_name;
1179 	}
1180 
1181 	// Update the dimension tags with actual image values
1182 
1183 	cairo_surface_t *surface = NULL;
1184 	int width = 0;
1185 	int height = 0;
1186 
1187 	if (image_data != NULL)
1188 		surface = gth_image_get_cairo_surface (image_data);
1189 
1190 	if (surface != NULL) {
1191 		width = cairo_image_surface_get_width (surface);
1192 		if (width > 0) {
1193 			ed["Exif.Photo.PixelXDimension"] = width;
1194 			ed["Exif.Image.ImageWidth"] = width;
1195 		}
1196 
1197 		height = cairo_image_surface_get_height (surface);
1198 		if (height > 0) {
1199 			ed["Exif.Photo.PixelYDimension"] = height;
1200 			ed["Exif.Image.ImageLength"] = height;
1201 		}
1202 
1203 		ed["Exif.Image.Orientation"] = 1;
1204 	}
1205 
1206 	// Update the thumbnail
1207 
1208 	Exiv2::ExifThumb thumb(ed);
1209 	if ((surface != NULL) && (width > 0) && (height > 0)) {
1210 		cairo_surface_t *thumbnail;
1211 		GthImage        *thumbnail_data;
1212 		char            *buffer;
1213 		gsize            buffer_size;
1214 
1215 		scale_keeping_ratio (&width, &height, 128, 128, FALSE);
1216 		thumbnail = _cairo_image_surface_scale (surface, width, height, SCALE_FILTER_BEST, NULL);
1217 		thumbnail_data = gth_image_new_for_surface (thumbnail);
1218 		if (gth_image_save_to_buffer (thumbnail_data,
1219 					      "image/jpeg",
1220 					      NULL,
1221 					      &buffer,
1222 					      &buffer_size,
1223 					      NULL,
1224 					      NULL))
1225 		{
1226 			thumb.setJpegThumbnail ((Exiv2::byte *) buffer, buffer_size);
1227 			ed["Exif.Thumbnail.XResolution"] = 72;
1228 			ed["Exif.Thumbnail.YResolution"] = 72;
1229 			ed["Exif.Thumbnail.ResolutionUnit"] =  2;
1230 			g_free (buffer);
1231 		}
1232 		else
1233 			thumb.erase();
1234 
1235 		g_object_unref (thumbnail_data);
1236 		cairo_surface_destroy (thumbnail);
1237 	}
1238 	else
1239 		thumb.erase();
1240 
1241 	if (surface != NULL)
1242 		cairo_surface_destroy (surface);
1243 
1244 	// Update the DateTime tag
1245 
1246 	if (g_file_info_get_attribute_object (info, "Exif::Image::DateTime") == NULL) {
1247 		GTimeVal current_time;
1248 		g_get_current_time (&current_time);
1249 		char *date_time = _g_time_val_to_exif_date (&current_time);
1250 		ed["Exif.Image.DateTime"] = date_time;
1251 		g_free (date_time);
1252 	}
1253 	ed.sortByKey();
1254 
1255 	// IPTC Data
1256 
1257 	Exiv2::IptcData id;
1258 	attributes = g_file_info_list_attributes (info, "Iptc");
1259 	for (i = 0; attributes[i] != NULL; i++) {
1260 		gpointer  metadatum = (GthMetadata *) g_file_info_get_attribute_object (info, attributes[i]);
1261 		char     *key = exiv2_key_from_attribute (attributes[i]);
1262 
1263 		try {
1264 			const char *value_type;
1265 
1266 			value_type = gth_main_get_metadata_type (metadatum, attributes[i]);
1267 			if (value_type != NULL) {
1268 				/* See the exif data code above for an explanation. */
1269 				Exiv2::Value::AutoPtr value = Exiv2::Value::create (Exiv2::TypeInfo::typeId (value_type));
1270 				Exiv2::IptcKey iptc_key(key);
1271 
1272 				const char *raw_value;
1273 
1274 				switch (gth_metadata_get_data_type (GTH_METADATA (metadatum))) {
1275 				case GTH_METADATA_TYPE_STRING:
1276 					raw_value = gth_metadata_get_raw (GTH_METADATA (metadatum));
1277 					if ((raw_value != NULL) && (strcmp (raw_value, "") != 0)) {
1278 						value->read (raw_value);
1279 						id.add (iptc_key, value.get());
1280 					}
1281 					break;
1282 
1283 				case GTH_METADATA_TYPE_STRING_LIST:
1284 					GthStringList *string_list = gth_metadata_get_string_list (GTH_METADATA (metadatum));
1285 					for (GList *scan = gth_string_list_get_list (string_list); scan; scan = scan->next) {
1286 						char *single_value = (char *) scan->data;
1287 
1288 						value->read (single_value);
1289 						id.add (iptc_key, value.get());
1290 					}
1291 					break;
1292 				}
1293 			}
1294 		}
1295 		catch (Exiv2::AnyError& e) {
1296 			/* we don't care about invalid key errors */
1297 			g_warning ("%s", e.what());
1298 		}
1299 
1300 		g_free (key);
1301 	}
1302 	id.sortByKey();
1303 	g_strfreev (attributes);
1304 
1305 	// XMP Data
1306 
1307 	Exiv2::XmpData xd;
1308 	attributes = g_file_info_list_attributes (info, "Xmp");
1309 	for (i = 0; attributes[i] != NULL; i++) {
1310 		gpointer  metadatum = (GthMetadata *) g_file_info_get_attribute_object (info, attributes[i]);
1311 		char     *key = exiv2_key_from_attribute (attributes[i]);
1312 
1313 		try {
1314 			const char *value_type;
1315 
1316 			value_type = gth_main_get_metadata_type (metadatum, attributes[i]);
1317 			if (value_type != NULL) {
1318 				/* See the exif data code above for an explanation. */
1319 				Exiv2::Value::AutoPtr value = Exiv2::Value::create (Exiv2::TypeInfo::typeId (value_type));
1320 				Exiv2::XmpKey xmp_key(key);
1321 
1322 				const char *raw_value;
1323 
1324 				switch (gth_metadata_get_data_type (GTH_METADATA (metadatum))) {
1325 				case GTH_METADATA_TYPE_STRING:
1326 					raw_value = gth_metadata_get_raw (GTH_METADATA (metadatum));
1327 					if ((raw_value != NULL) && (strcmp (raw_value, "") != 0)) {
1328 						value->read (raw_value);
1329 						xd.add (xmp_key, value.get());
1330 					}
1331 					break;
1332 
1333 				case GTH_METADATA_TYPE_STRING_LIST:
1334 					GthStringList *string_list = gth_metadata_get_string_list (GTH_METADATA (metadatum));
1335 					for (GList *scan = gth_string_list_get_list (string_list); scan; scan = scan->next) {
1336 						char *single_value = (char *) scan->data;
1337 
1338 						value->read (single_value);
1339 						xd.add (xmp_key, value.get());
1340 					}
1341 					break;
1342 				}
1343 			}
1344 		}
1345 		catch (Exiv2::AnyError& e) {
1346 			/* we don't care about invalid key errors */
1347 			g_warning ("%s", e.what());
1348 		}
1349 
1350 		g_free (key);
1351 	}
1352 	xd.sortByKey();
1353 	g_strfreev (attributes);
1354 
1355 	try {
1356 		image->setExifData(ed);
1357 		image->setIptcData(id);
1358 		image->setXmpData(xd);
1359 		image->writeMetadata();
1360 	}
1361 	catch (Exiv2::AnyError& e) {
1362 		g_warning ("%s", e.what());
1363 	}
1364 
1365 	Exiv2::BasicIo &io = image->io();
1366 	io.open();
1367 
1368 	return io.read(io.size());
1369 }
1370 
1371 
1372 extern "C"
1373 gboolean
exiv2_supports_writes(const char * mime_type)1374 exiv2_supports_writes (const char *mime_type)
1375 {
1376 	return (g_content_type_equals (mime_type, "image/jpeg")
1377 		|| g_content_type_equals (mime_type, "image/tiff")
1378 		|| g_content_type_equals (mime_type, "image/png"));
1379 }
1380 
1381 
1382 extern "C"
1383 gboolean
exiv2_write_metadata(GthImageSaveData * data)1384 exiv2_write_metadata (GthImageSaveData *data)
1385 {
1386 	if (exiv2_supports_writes (data->mime_type) && (data->file_data != NULL)) {
1387 		try {
1388 			Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open ((Exiv2::byte*) data->buffer, data->buffer_size);
1389 			g_assert (image.get() != 0);
1390 
1391 			Exiv2::DataBuf buf = exiv2_write_metadata_private (image, data->file_data->info, data->image);
1392 
1393 			g_free (data->buffer);
1394 			data->buffer = g_memdup (buf.pData_, buf.size_);
1395 			data->buffer_size = buf.size_;
1396 		}
1397 		catch (Exiv2::AnyError& e) {
1398 			if (data->error != NULL)
1399 				*data->error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, e.what());
1400 			g_warning ("%s\n", e.what());
1401 			return FALSE;
1402 		}
1403 	}
1404 
1405 	return TRUE;
1406 }
1407 
1408 
1409 extern "C"
1410 gboolean
exiv2_write_metadata_to_buffer(void ** buffer,gsize * buffer_size,GFileInfo * info,GthImage * image_data,GError ** error)1411 exiv2_write_metadata_to_buffer (void      **buffer,
1412 				gsize      *buffer_size,
1413 				GFileInfo  *info,
1414 				GthImage   *image_data,
1415 				GError    **error)
1416 {
1417 	try {
1418 		Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open ((Exiv2::byte*) *buffer, *buffer_size);
1419 		g_assert (image.get() != 0);
1420 
1421 		Exiv2::DataBuf buf = exiv2_write_metadata_private (image, info, image_data);
1422 
1423 		g_free (*buffer);
1424 		*buffer = g_memdup (buf.pData_, buf.size_);
1425 		*buffer_size = buf.size_;
1426 	}
1427 	catch (Exiv2::AnyError& e) {
1428 		if (error != NULL)
1429 			*error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, e.what());
1430 		return FALSE;
1431 	}
1432 
1433 	return TRUE;
1434 }
1435 
1436 
1437 extern "C"
1438 gboolean
exiv2_clear_metadata(void ** buffer,gsize * buffer_size,GError ** error)1439 exiv2_clear_metadata (void   **buffer,
1440 		      gsize   *buffer_size,
1441 		      GError **error)
1442 {
1443 	try {
1444 		Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open ((Exiv2::byte*) *buffer, *buffer_size);
1445 
1446 		if (image.get() == 0) {
1447 			if (error != NULL)
1448 				*error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, _("Invalid file format"));
1449 			return FALSE;
1450 		}
1451 
1452 		try {
1453 			image->clearMetadata();
1454 			image->writeMetadata();
1455 		}
1456 		catch (Exiv2::AnyError& e) {
1457 			g_warning ("%s", e.what());
1458 		}
1459 
1460 		Exiv2::BasicIo &io = image->io();
1461 		io.open();
1462 		Exiv2::DataBuf buf = io.read(io.size());
1463 
1464 		g_free (*buffer);
1465 		*buffer = g_memdup (buf.pData_, buf.size_);
1466 		*buffer_size = buf.size_;
1467 	}
1468 	catch (Exiv2::AnyError& e) {
1469 		if (error != NULL)
1470 			*error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, e.what());
1471 		return FALSE;
1472 	}
1473 
1474 	return TRUE;
1475 }
1476 
1477 
1478 #define MAX_RATIO_ERROR_TOLERANCE 0.01
1479 
1480 
1481 GdkPixbuf *
exiv2_generate_thumbnail(const char * uri,const char * mime_type,int requested_size)1482 exiv2_generate_thumbnail (const char *uri,
1483 			  const char *mime_type,
1484 			  int         requested_size)
1485 {
1486 	GdkPixbuf *pixbuf = NULL;
1487 
1488 	if (! _g_content_type_is_a (mime_type, "image/jpeg")
1489 	    && ! _g_content_type_is_a (mime_type, "image/tiff"))
1490 	{
1491 		return NULL;
1492 	}
1493 
1494 	try {
1495 		char *path;
1496 
1497 		path = g_filename_from_uri (uri, NULL, NULL);
1498 		if (path == NULL)
1499 			return NULL;
1500 
1501 		Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open (path);
1502 		image->readMetadata ();
1503 		Exiv2::ExifThumbC exifThumb (image->exifData ());
1504 		Exiv2::DataBuf thumb = exifThumb.copy ();
1505 
1506 		g_free (path);
1507 
1508 		if (thumb.pData_ == NULL)
1509 			return NULL;
1510 
1511 		Exiv2::ExifData &ed = image->exifData();
1512 
1513 		long orientation = (ed["Exif.Image.Orientation"].count() > 0) ? ed["Exif.Image.Orientation"].toLong() : 1;
1514 		long image_width = (ed["Exif.Photo.PixelXDimension"].count() > 0) ? ed["Exif.Photo.PixelXDimension"].toLong() : -1;
1515 		long image_height = (ed["Exif.Photo.PixelYDimension"].count() > 0) ? ed["Exif.Photo.PixelYDimension"].toLong() : -1;
1516 
1517 		if ((orientation != 1) || (image_width <= 0) || (image_height <= 0))
1518 			return NULL;
1519 
1520 		GInputStream *stream = g_memory_input_stream_new_from_data (thumb.pData_, thumb.size_, NULL);
1521 		pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, NULL);
1522 		g_object_unref (stream);
1523 
1524 		if (pixbuf == NULL)
1525 			return NULL;
1526 
1527 		/* Heuristic to find out-of-date thumbnails: the thumbnail and image aspect ratios must be equal */
1528 
1529 		int    pixbuf_width = gdk_pixbuf_get_width (pixbuf);
1530 		int    pixbuf_height = gdk_pixbuf_get_height (pixbuf);
1531 		double image_ratio = (((double) image_width) / image_height);
1532 		double thumbnail_ratio = (((double) pixbuf_width) / pixbuf_height);
1533 		double ratio_delta = (image_ratio > thumbnail_ratio) ? (image_ratio - thumbnail_ratio) : (thumbnail_ratio - image_ratio);
1534 
1535 		if ((ratio_delta > MAX_RATIO_ERROR_TOLERANCE) /* the tolerance is used because the reduced image can have a slightly different ratio due to rounding errors */
1536 		    || (MAX (pixbuf_width, pixbuf_height) < requested_size)) /* ignore the embedded image if it's too small compared to the requested size */
1537 		{
1538 			g_object_unref (pixbuf);
1539 			return NULL;
1540 		}
1541 
1542 		/* Scale the pixbuf to perfectly fit the requested size */
1543 
1544 		if (scale_keeping_ratio (&pixbuf_width,
1545 					 &pixbuf_height,
1546 					 requested_size,
1547 					 requested_size,
1548 					 TRUE))
1549 		{
1550 			GdkPixbuf *tmp = pixbuf;
1551 			pixbuf = _gdk_pixbuf_scale_simple_safe (tmp, pixbuf_width, pixbuf_height, GDK_INTERP_BILINEAR);
1552 			g_object_unref (tmp);
1553 		}
1554 
1555 		/* Save the original image size in the pixbuf options */
1556 
1557 		char *s = g_strdup_printf ("%ld", image_width);
1558 		gdk_pixbuf_set_option (pixbuf, "tEXt::Thumb::Image::Width", s);
1559 		g_object_set_data (G_OBJECT (pixbuf), "gnome-original-width", GINT_TO_POINTER ((int) image_width));
1560 		g_free (s);
1561 
1562 		s = g_strdup_printf ("%ld", image_height);
1563 		gdk_pixbuf_set_option (pixbuf, "tEXt::Thumb::Image::Height", s);
1564 		g_object_set_data (G_OBJECT (pixbuf), "gnome-original-height", GINT_TO_POINTER ((int) image_height));
1565 		g_free (s);
1566 
1567 		/* Set the orientation option to correctly rotate the thumbnail
1568 		 * in gnome_desktop_thumbnail_factory_generate_thumbnail() */
1569 
1570 		char *orientation_s = g_strdup_printf ("%ld", orientation);
1571 		gdk_pixbuf_set_option (pixbuf, "orientation", orientation_s);
1572 		g_free (orientation_s);
1573 	}
1574 	catch (Exiv2::AnyError& e) {
1575 	}
1576 
1577 	return pixbuf;
1578 }
1579