gtk_label_set_text(GTK_LABEL(bd->label_file_time), (bd->fd) ? text_from_time(bd->fd->date) : "");
}
- if (metadata_read(bd->fd, &keywords, &comment))
- {
- keyword_list_push(bd->keyword_view, keywords);
- gtk_text_buffer_set_text(comment_buffer,
- (comment) ? comment : "", -1);
-
- bar_keyword_list_sync(bd, keywords);
-
- string_list_free(keywords);
- g_free(comment);
- }
- else
- {
- gtk_text_buffer_set_text(keyword_buffer, "", -1);
- gtk_text_buffer_set_text(comment_buffer, "", -1);
-
- bar_keyword_list_sync(bd, NULL);
- }
-
+ comment = metadata_read_string(bd->fd, COMMENT_KEY);
+ gtk_text_buffer_set_text(comment_buffer,
+ (comment) ? comment : "", -1);
+ g_free(comment);
+
+ keywords = metadata_read_list(bd->fd, KEYWORD_KEY);
+ keyword_list_push(bd->keyword_view, keywords);
+ bar_keyword_list_sync(bd, keywords);
+ string_list_free(keywords);
+
g_signal_handlers_unblock_by_func(keyword_buffer, bar_info_changed, bd);
g_signal_handlers_unblock_by_func(comment_buffer, bar_info_changed, bd);
FileData *fd = work->data;
work = work->next;
- metadata_set(fd, keywords, comment, append);
+ if (append)
+ {
+ if (comment) metadata_append_string(fd, COMMENT_KEY, comment);
+ if (keywords) metadata_append_list(fd, KEYWORD_KEY, keywords);
+ }
+ else
+ {
+ if (comment) metadata_write_string(fd, COMMENT_KEY, comment);
+ if (keywords) metadata_write_list(fd, KEYWORD_KEY, keywords);
+ }
}
filelist_free(list);
}
break;
case EXIF_FORMAT_STRING:
- string = g_string_append(string, (gchar *)(item->data));
+ if (item->data) string = g_string_append(string, (gchar *)(item->data));
break;
case EXIF_FORMAT_SHORT_UNSIGNED:
if (ne == 1 && marker->list)
return 0;
}
+GList *exif_get_metadata(ExifData *exif, const gchar *key)
+{
+ gchar *str;
+ ExifItem *item = exif_get_item(exif, key);
+ if (!item) return NULL;
+
+ str = exif_item_get_string(item, 0);
+
+ if (!str) return NULL;
+
+ return g_list_append(NULL, str);
+}
+
typedef struct _UnmapData UnmapData;
struct _UnmapData
{
gchar *exif_get_formatted_by_key(ExifData *exif, const gchar *key, gint *key_valid);
gint exif_update_metadata(ExifData *exif, const gchar *key, const GList *values);
+GList *exif_get_metadata(ExifData *exif, const gchar *key);
guchar *exif_get_color_profile(ExifData *exif, guint *data_len);
#include <exiv2/xmpsidecar.hpp>
#endif
-
extern "C" {
#include <glib.h>
#include "filefilter.h"
#include "ui_fileops.h"
+
+#include "misc.h"
}
+typedef struct _AltKey AltKey;
+
+struct _AltKey
+{
+ const gchar *xmp_key;
+ const gchar *exif_key;
+ const gchar *iptc_key;
+};
+
+/* this is a list of keys that should be converted, even with the older Exiv2 which does not support it directly */
+static const AltKey alt_keys[] = {
+ {"Xmp.tiff.Orientation", "Exif.Image.Orientation", NULL},
+ {"Xmp.dc.subject", NULL, "Iptc.Application2.Keywords"},
+ {"Xmp.dc.description", NULL, "Iptc.Application2.Caption"},
+ {NULL, NULL, NULL}
+ };
+
+
struct _ExifData
{
Exiv2::ExifData::const_iterator exifIter; /* for exif_get_next_item */
virtual void writeMetadata(gchar *path = NULL)
{
-#if EXIV2_TEST_VERSION(0,17,0)
- syncExifWithXmp(exifData_, xmpData_);
-#endif
if (!path)
{
#if EXIV2_TEST_VERSION(0,17,0)
- if (options->metadata.save_legacy_IPTC) copyXmpToIptc(xmpData_, iptcData_);
+ if (options->metadata.save_legacy_IPTC)
+ copyXmpToIptc(xmpData_, iptcData_);
+ else
+ iptcData_.clear();
+
+ copyXmpToExif(xmpData_, exifData_);
#endif
imageData_->image()->setExifData(exifData_);
imageData_->image()->setIptcData(iptcData_);
}
}
-gint exif_update_metadata(ExifData *exif, const gchar *key, const GList *values)
+static const AltKey *find_alt_key(const gchar *xmp_key)
+{
+ gint i = 0;
+
+ while (alt_keys[i].xmp_key)
+ {
+ if (strcmp(xmp_key, alt_keys[i].xmp_key) == 0) return &alt_keys[i];
+ i++;
+ }
+ return NULL;
+}
+
+static gint exif_update_metadata_simple(ExifData *exif, const gchar *key, const GList *values)
{
try {
const GList *work = values;
exif->xmpData()[key] = (gchar *)work->data;
work = work->next;
}
+#else
+ throw e;
#endif
}
}
}
}
+gint exif_update_metadata(ExifData *exif, const gchar *key, const GList *values)
+{
+ gint ret = exif_update_metadata_simple(exif, key, values);
+
+ if (
+#if !EXIV2_TEST_VERSION(0,17,0)
+ TRUE || /* no conversion support */
+#endif
+ !values || /* deleting item */
+ !ret /* writing to the explicitely given xmp tag failed */
+ )
+ {
+ /* deleted xmp metadatum can't be converted, we have to delete also the corresponding legacy tag */
+ /* if we can't write xmp, update at least the legacy tag */
+ const AltKey *alt_key = find_alt_key(key);
+ if (alt_key && alt_key->iptc_key)
+ ret = exif_update_metadata_simple(exif, alt_key->iptc_key, values);
+
+ if (alt_key && alt_key->exif_key)
+ ret = exif_update_metadata_simple(exif, alt_key->exif_key, values);
+ }
+ return ret;
+}
+
+
+static GList *exif_add_value_to_glist(GList *list, Exiv2::Metadatum &item)
+{
+ Exiv2::TypeId id = item.typeId();
+ if (id == Exiv2::asciiString ||
+ id == Exiv2::undefined ||
+ id == Exiv2::string ||
+ id == Exiv2::date ||
+ id == Exiv2::time ||
+#if EXIV2_TEST_VERSION(0,16,0)
+ id == Exiv2::xmpText ||
+ id == Exiv2::langAlt ||
+#endif
+ id == Exiv2::comment
+ )
+ {
+ /* read as a single entry */
+ std::string str = item.toString();
+ if (str.length() > 5 && str.substr(0, 5) == "lang=")
+ {
+ std::string::size_type pos = str.find_first_of(' ');
+ if (pos != std::string::npos) str = str.substr(pos+1);
+ }
+ list = g_list_append(list, utf8_validate_or_convert(str.c_str()));
+ }
+ else
+ {
+ /* read as a list */
+ gint i;
+ for (i = 0; i < item.count(); i++)
+ list = g_list_append(list, utf8_validate_or_convert(item.toString(i).c_str()));
+ }
+ return list;
+}
+
+static GList *exif_get_metadata_simple(ExifData *exif, const gchar *key)
+{
+ GList *list = NULL;
+ try {
+ try {
+ Exiv2::ExifKey ekey(key);
+
+ Exiv2::ExifData::iterator pos = exif->exifData().findKey(ekey);
+ if (pos != exif->exifData().end())
+ list = exif_add_value_to_glist(list, *pos);
+
+ }
+ catch (Exiv2::AnyError& e) {
+ try {
+ Exiv2::IptcKey ekey(key);
+ Exiv2::IptcData::iterator pos = exif->iptcData().begin();
+ while (pos != exif->iptcData().end())
+ {
+ if (pos->key() == key)
+ list = exif_add_value_to_glist(list, *pos);
+ ++pos;
+ }
+
+ }
+ catch (Exiv2::AnyError& e) {
+#if EXIV2_TEST_VERSION(0,16,0)
+ Exiv2::XmpKey ekey(key);
+ Exiv2::XmpData::iterator pos = exif->xmpData().findKey(ekey);
+ if (pos != exif->xmpData().end())
+ list = exif_add_value_to_glist(list, *pos);
+#endif
+ }
+ }
+ }
+ catch (Exiv2::AnyError& e) {
+ std::cout << "Caught Exiv2 exception '" << e << "'\n";
+ }
+ return list;
+}
+
+GList *exif_get_metadata(ExifData *exif, const gchar *key)
+{
+ GList *list = NULL;
+
+ list = exif_get_metadata_simple(exif, key);
+
+ /* the following code can be ifdefed out as soon as Exiv2 supports it */
+ if (!list)
+ {
+ const AltKey *alt_key = find_alt_key(key);
+ if (alt_key && alt_key->iptc_key)
+ list = exif_get_metadata_simple(exif, alt_key->iptc_key);
+
+#if !EXIV2_TEST_VERSION(0,17,0)
+ /* with older Exiv2 versions exif is not synced */
+ if (!list && alt_key && alt_key->exif_key)
+ list = exif_get_metadata_simple(exif, alt_key->exif_key);
+#endif
+ }
+ return list;
+}
+
void exif_add_jpeg_color_profile(ExifData *exif, unsigned char *cp_data, guint cp_length)
{
g_assert(fd);
- if (metadata_read(fd, &keywords, NULL))
+ keywords = metadata_read_list(fd, KEYWORD_KEY);
+
+ if (keywords)
{
GList *work = keywords;
g_string_append(kwstr, kw);
}
+ string_list_free(keywords);
}
if (kwstr)
}
else if (strcmp(name, "comment") == 0)
{
- metadata_read(imd->image_fd, NULL, &data);
+ data = metadata_read_string(imd->image_fd, COMMENT_KEY);
}
else
{
fclose(f);
- *keywords = g_list_reverse(list);
+ if (keywords)
+ {
+ *keywords = g_list_reverse(list);
+ }
+ else
+ {
+ string_list_free(list);
+ }
+
if (comment_build)
{
if (comment)
return g_list_reverse(newlist);
}
-
-static gint metadata_xmp_read(FileData *fd, GList **keywords, gchar **comment)
+GList *metadata_read_list(FileData *fd, const gchar *key)
{
ExifData *exif;
+ GList *list = NULL;
+ if (!fd) return NULL;
- exif = exif_read_fd(fd);
- if (!exif) return FALSE;
-
- if (comment)
+ /* unwritten data overide everything */
+ if (fd->modified_xmp)
{
- gchar *text;
- ExifItem *item = exif_get_item(exif, COMMENT_KEY);
-
- text = exif_item_get_string(item, 0);
- *comment = utf8_validate_or_convert(text);
- g_free(text);
+ list = g_hash_table_lookup(fd->modified_xmp, key);
+ if (list) return string_list_copy(list);
}
- if (keywords)
+ /*
+ Legacy metadata file is the primary source if it exists.
+ Merging the lists does not make much sense, because the existence of
+ legacy metadata file indicates that the other metadata sources are not
+ writable and thus it would not be possible to delete the keywords
+ that comes from the image file.
+ */
+ if (strcmp(key, KEYWORD_KEY) == 0)
{
- ExifItem *item;
- guint i;
-
- *keywords = NULL;
- item = exif_get_item(exif, KEYWORD_KEY);
- for (i = 0; i < exif_item_get_elements(item); i++)
- {
- gchar *kw = exif_item_get_string(item, i);
- gchar *utf8_kw;
-
- if (!kw) break;
-
- utf8_kw = utf8_validate_or_convert(kw);
- *keywords = g_list_append(*keywords, (gpointer) utf8_kw);
- g_free(kw);
- }
-
- /* FIXME:
- * Exiv2 handles Iptc keywords as multiple entries with the
- * same key, thus exif_get_item returns only the first keyword
- * and the only way to get all keywords is to iterate through
- * the item list.
- */
- /* Read IPTC keywords only if there are no XMP keywords
- * IPTC does not have standard charset, thus the encoding may differ
- * from XMP and keyword merging is not reliable.
- */
- if (!*keywords)
- {
- for (item = exif_get_first_item(exif);
- item;
- item = exif_get_next_item(exif))
- {
- guint tag;
-
- tag = exif_item_get_tag_id(item);
- if (tag == 0x0019)
- {
- gchar *tag_name = exif_item_get_tag_name(item);
-
- if (strcmp(tag_name, "Iptc.Application2.Keywords") == 0)
- {
- gchar *kw;
- gchar *utf8_kw;
-
- kw = exif_item_get_data_as_text(item);
- if (!kw) continue;
-
- utf8_kw = utf8_validate_or_convert(kw);
- *keywords = g_list_append(*keywords, (gpointer) utf8_kw);
- g_free(kw);
- }
- g_free(tag_name);
- }
- }
- }
- }
+ if (metadata_legacy_read(fd, &list, NULL)) return list;
+ }
+ if (strcmp(key, COMMENT_KEY) == 0)
+ {
+ gchar *comment = NULL;
+ if (metadata_legacy_read(fd, NULL, &comment)) return g_list_append(NULL, comment);
+ }
+
+ exif = exif_read_fd(fd); /* this is cached, thus inexpensive */
+ if (!exif) return NULL;
+ list = exif_get_metadata(exif, key);
exif_free_fd(fd, exif);
-
- return (comment && *comment) || (keywords && *keywords);
+ return list;
}
-gint metadata_read(FileData *fd, GList **keywords, gchar **comment)
+gchar *metadata_read_string(FileData *fd, const gchar *key)
{
- GList *keywords_xmp = NULL;
- GList *keywords_legacy = NULL;
- gchar *comment_xmp = NULL;
- gchar *comment_legacy = NULL;
- gint result_xmp, result_legacy;
-
- if (!fd) return FALSE;
-
- result_xmp = metadata_xmp_read(fd, &keywords_xmp, &comment_xmp);
- result_legacy = metadata_legacy_read(fd, &keywords_legacy, &comment_legacy);
-
- if (!result_xmp && !result_legacy)
+ GList *string_list = metadata_read_list(fd, key);
+ if (string_list)
{
- return FALSE;
+ gchar *str = string_list->data;
+ string_list->data = NULL;
+ string_list_free(string_list);
+ return str;
}
-
- if (keywords)
+ return NULL;
+}
+
+gboolean metadata_append_string(FileData *fd, const gchar *key, const char *value)
+{
+ gchar *str = metadata_read_string(fd, key);
+
+ if (!str)
{
- if (result_xmp && result_legacy)
- *keywords = g_list_concat(keywords_xmp, keywords_legacy);
- else
- *keywords = result_xmp ? keywords_xmp : keywords_legacy;
-
- *keywords = remove_duplicate_strings_from_list(*keywords);
+ return metadata_write_string(fd, key, value);
}
else
{
- if (result_xmp) string_list_free(keywords_xmp);
- if (result_legacy) string_list_free(keywords_legacy);
- }
-
-
- if (comment)
- {
- if (result_xmp && result_legacy && comment_xmp && comment_legacy && *comment_xmp && *comment_legacy)
- *comment = g_strdup_printf("%s\n%s", comment_xmp, comment_legacy);
- else
- *comment = result_xmp ? comment_xmp : comment_legacy;
+ gchar *new_string = g_strconcat(str, value, NULL);
+ gboolean ret = metadata_write_string(fd, key, new_string);
+ g_free(str);
+ g_free(new_string);
+ return ret;
}
-
- if (result_xmp && (!comment || *comment != comment_xmp)) g_free(comment_xmp);
- if (result_legacy && (!comment || *comment != comment_legacy)) g_free(comment_legacy);
-
- // return FALSE in the following cases:
- // - only looking for a comment and didn't find one
- // - only looking for keywords and didn't find any
- // - looking for either a comment or keywords, but found nothing
- if ((!keywords && comment && !*comment) ||
- (!comment && keywords && !*keywords) ||
- ( comment && !*comment && keywords && !*keywords))
- return FALSE;
-
- return TRUE;
}
-void metadata_set(FileData *fd, GList *new_keywords, gchar *new_comment, gboolean append)
+gboolean metadata_append_list(FileData *fd, const gchar *key, const GList *values)
{
- gchar *comment = NULL;
- GList *keywords = NULL;
- GList *keywords_list = NULL;
-
- metadata_read(fd, &keywords, &comment);
+ GList *list = metadata_read_list(fd, key);
- if (new_comment)
+ if (!list)
{
- if (append && comment && *comment)
- {
- gchar *tmp = comment;
-
- comment = g_strconcat(tmp, new_comment, NULL);
- g_free(tmp);
- }
- else
- {
- g_free(comment);
- comment = g_strdup(new_comment);
- }
+ return metadata_write_list(fd, key, values);
}
-
- if (new_keywords)
+ else
{
- if (append && keywords && g_list_length(keywords) > 0)
- {
- GList *work;
-
- work = new_keywords;
- while (work)
- {
- gchar *key;
- GList *p;
-
- key = work->data;
- work = work->next;
-
- p = keywords;
- while (p && key)
- {
- gchar *needle = p->data;
- p = p->next;
-
- if (strcmp(needle, key) == 0) key = NULL;
- }
-
- if (key) keywords = g_list_append(keywords, g_strdup(key));
- }
- keywords_list = keywords;
- }
- else
- {
- keywords_list = new_keywords;
- }
+ gboolean ret;
+ list = g_list_concat(list, string_list_copy(values));
+ list = remove_duplicate_strings_from_list(list);
+
+ ret = metadata_write_list(fd, key, list);
+ string_list_free(list);
+ return ret;
}
-
- metadata_write_string(fd, COMMENT_KEY, comment);
- metadata_write_list(fd, KEYWORD_KEY, keywords);
-
- string_list_free(keywords);
- g_free(comment);
}
gboolean find_string_in_list(GList *list, const gchar *string)
{
GList *keywords;
gboolean found = FALSE;
- if (metadata_read(fd, &keywords, NULL))
+ keywords = metadata_read_list(fd, KEYWORD_KEY);
+ if (keywords)
{
GList *work = keywords;
gboolean found = FALSE;
gboolean changed = FALSE;
GList *work;
- metadata_read(fd, &keywords, NULL);
+ keywords = metadata_read_list(fd, KEYWORD_KEY);
work = keywords;
gboolean metadata_write_list(FileData *fd, const gchar *key, const GList *values);
gboolean metadata_write_string(FileData *fd, const gchar *key, const char *value);
-gint metadata_read(FileData *fd, GList **keywords, gchar **comment);
+GList *metadata_read_list(FileData *fd, const gchar *key);
+gchar *metadata_read_string(FileData *fd, const gchar *key);
+
+gboolean metadata_append_string(FileData *fd, const gchar *key, const char *value);
+gboolean metadata_append_list(FileData *fd, const gchar *key, const GList *values);
-void metadata_set(FileData *fd, GList *new_keywords, gchar *new_comment, gboolean append);
gboolean find_string_in_list(GList *list, const gchar *keyword);
GList *string_to_keywords_list(const gchar *text);
tested = TRUE;
match = FALSE;
- if (metadata_read(fd, &list, NULL))
+ list = metadata_read_list(fd, KEYWORD_KEY);
+
+ if (list)
{
GList *needle;
GList *haystack;
tested = TRUE;
match = FALSE;
- if (metadata_read(fd, NULL, &comment))
+ comment = metadata_read_string(fd, COMMENT_KEY);
+
+ if (comment)
{
if (! sd->search_comment_match_case)
{
g_list_free(list);
}
-GList *string_list_copy(GList *list)
+GList *string_list_copy(const GList *list)
{
GList *new_list = NULL;
GList *work;
* the lists with string_list_free()
*/
void string_list_free(GList *list);
-GList *string_list_copy(GList *list);
+GList *string_list_copy(const GList *list);
gchar *unique_filename(const gchar *path, const gchar *ext, const gchar *divider, gint pad);
gchar *unique_filename_simple(const gchar *path);
gchar *str = g_strndup(selection->data, selection->length);
GList *kw_list = string_to_keywords_list(str);
- metadata_set(fd, kw_list, NULL, TRUE);
+ metadata_append_list(fd, KEYWORD_KEY, kw_list);
string_list_free(kw_list);
g_free(str);
if (vf->layout && vf->layout->bar_info) {
gchar *str = g_strndup(selection->data, selection->length);
GList *kw_list = string_to_keywords_list(str);
- metadata_set(fd, kw_list, NULL, TRUE);
+ metadata_append_list(fd, KEYWORD_KEY, kw_list);
string_list_free(kw_list);
g_free(str);
if (vf->layout && vf->layout->bar_info) {