From 40b2412fa4674e14541c407f2c2b5d5c93c0afd1 Mon Sep 17 00:00:00 2001 From: Jacob Boerema Date: Mon, 14 Aug 2023 17:29:44 -0400 Subject: [PATCH] plug-ins: add synchronization with certain Exif tags. Exif tags Exif.Image.Artist, Exif.Image.ImageDescription, and Exif.Image.Copyright were not being synchronized with their Xmp and Iptc.IIM equivalents in the metadata-editor. This commit adds synchronization of reading and writing of the metadata. We adjust the struct for equivalent_metadata_tags by removing unused fields and adding a field that points to the equivalent Exif tag in a new struct called exif_tag_info. On loading of the metadata tags we now also check a limited set of Exif tags. If no value was set yet, we use the Exif value, or else we check to make sure both had the same value. If not, print a warning in the terminal. On saving of metadata, we now also save to the marked Exif tags, or if the value is NULL, we remove the tag. In passing, we also fix two memory leaks by freeing with g_strfreev temporary string lists. --- plug-ins/metadata/metadata-editor.c | 90 +++++++++++++++++++++++++++++ plug-ins/metadata/metadata-impexp.c | 2 +- plug-ins/metadata/metadata-misc.h | 15 +++++ plug-ins/metadata/metadata-tags.c | 53 +++++++++-------- plug-ins/metadata/metadata-tags.h | 10 ++-- 5 files changed, 142 insertions(+), 28 deletions(-) diff --git a/plug-ins/metadata/metadata-editor.c b/plug-ins/metadata/metadata-editor.c index 63011f99a8..0e6ed22363 100644 --- a/plug-ins/metadata/metadata-editor.c +++ b/plug-ins/metadata/metadata-editor.c @@ -2826,6 +2826,37 @@ metadata_dialog_editor_set_metadata (GExiv2Metadata *metadata, { gchar **equiv_values; + if (equivalent_metadata_tags[index].exif_tag_index > -1) + { + gint32 i_exif = equivalent_metadata_tags[index].exif_tag_index; + const gchar *exif_tag_str = exif_equivalent_tags[i_exif].tag; + gchar *exif_value = NULL; + + exif_value = gexiv2_metadata_try_get_tag_interpreted_string (metadata, + exif_tag_str, + NULL); + + if (exif_value) + { + if (! value) + { + value = exif_value; + exif_value = NULL; + } + else + { + if (g_strcmp0 (value, exif_value)) + { + g_printerr ("Value of tag %s: '%s' is not the same as %s: '%s'. " + "Ignoring value of %s.\n", + default_metadata_tags[equivalent_metadata_tags[index].default_tag_index].tag, + value, exif_tag_str, exif_value, exif_tag_str); + } + } + } + g_free (exif_value); + } + /* These are all IPTC tags some of which can appear multiple times so * we will use get_tag_multiple. Also IPTC most commonly uses UTF-8 * not current locale so get_tag_interpreted was wrong anyway. @@ -2861,6 +2892,7 @@ metadata_dialog_editor_set_metadata (GExiv2Metadata *metadata, } } } + g_strfreev (equiv_values); } } g_strfreev (values); @@ -2999,6 +3031,38 @@ metadata_dialog_editor_set_metadata (GExiv2Metadata *metadata, { gchar **values; + if (equivalent_metadata_tags[index].exif_tag_index > -1) + { + gint32 i_exif = equivalent_metadata_tags[index].exif_tag_index; + const gchar *exif_tag_str = exif_equivalent_tags[i_exif].tag; + gchar *exif_value = NULL; + + exif_value = gexiv2_metadata_try_get_tag_interpreted_string (metadata, + exif_tag_str, + NULL); + + if (exif_value) + { + + if (! value) + { + value = exif_value; + exif_value = NULL; + } + else + { + if (g_strcmp0 (value, exif_value)) + { + g_printerr ("Value of tag %s: '%s' is not the same as %s: '%s'. " + "Ignoring value of %s.\n", + default_metadata_tags[equivalent_metadata_tags[index].default_tag_index].tag, + value, exif_tag_str, exif_value, exif_tag_str); + } + } + } + g_free (exif_value); + } + /* It's not very likely we will have an XMP tag that can only * have a single value instead of an array, which corresponds to * an IPTC tag that can have multiple values, but since we @@ -3059,6 +3123,7 @@ metadata_dialog_editor_set_metadata (GExiv2Metadata *metadata, } value = g_string_free (str, FALSE); } + g_strfreev (values); } } } @@ -5078,6 +5143,19 @@ metadata_editor_write_callback (GtkWidget *dialog, if (*text_value) set_tag_string (g_metadata, equivalent_metadata_tags[index].tag, text_value, FALSE); + + if (equivalent_metadata_tags[index].exif_tag_index > -1) + { + gint i_exif = equivalent_metadata_tags[index].exif_tag_index; + const gchar *exif_tag_str = exif_equivalent_tags[i_exif].tag; + + gexiv2_metadata_try_clear_tag (GEXIV2_METADATA (g_metadata), + exif_tag_str, + NULL); + if (*text_value) + set_tag_string (g_metadata, exif_tag_str, + text_value, FALSE); + } } } } @@ -5178,6 +5256,18 @@ metadata_editor_write_callback (GtkWidget *dialog, equivalent_metadata_tags[index].tag); } } + + if (equivalent_metadata_tags[index].exif_tag_index > -1) + { + gint i_exif = equivalent_metadata_tags[index].exif_tag_index; + const gchar *exif_tag_str = exif_equivalent_tags[i_exif].tag; + + gexiv2_metadata_try_clear_tag (GEXIV2_METADATA (g_metadata), + exif_tag_str, + NULL); + if (text && *text) + set_tag_string (g_metadata, exif_tag_str, text, FALSE); + } } if (text) diff --git a/plug-ins/metadata/metadata-impexp.c b/plug-ins/metadata/metadata-impexp.c index c0e6a4709a..abc1e86885 100644 --- a/plug-ins/metadata/metadata-impexp.c +++ b/plug-ins/metadata/metadata-impexp.c @@ -125,7 +125,7 @@ export_file_metadata (metadata_editor *args) /* HANDLE IPTC */ for (i = 0; i < n_equivalent_metadata_tags; i++) { - int index = equivalent_metadata_tags[i].other_tag_index; + int index = equivalent_metadata_tags[i].default_tag_index; g_string_append (xmldata, "\t\n"); g_string_append (xmldata, "\t\t"); g_string_append (xmldata, equivalent_metadata_tags[i].tag); diff --git a/plug-ins/metadata/metadata-misc.h b/plug-ins/metadata/metadata-misc.h index ad638db14d..f37517c13a 100644 --- a/plug-ins/metadata/metadata-misc.h +++ b/plug-ins/metadata/metadata-misc.h @@ -46,6 +46,21 @@ typedef struct gint32 xmp_type; } metadata_tag; +typedef struct +{ + gchar *tag; + MetadataMode mode; + gint32 default_tag_index; + gint32 exif_tag_index; +} iptc_tag_info; + +typedef struct +{ + gint32 xmp_equivalent_index; + gchar *tag; + MetadataMode mode; +} exif_tag_info; + typedef struct { gchar *data; diff --git a/plug-ins/metadata/metadata-tags.c b/plug-ins/metadata/metadata-tags.c index 834682dbfc..ff5f205da5 100644 --- a/plug-ins/metadata/metadata-tags.c +++ b/plug-ins/metadata/metadata-tags.c @@ -145,33 +145,40 @@ const gint n_default_metadata_tags = G_N_ELEMENTS (default_metadata_tags); * MODE_SINGLE - for iptc tags that can appear only once, * MODE_MULTI - for iptc tags that are repeatable, i.e. can appear multiple times. */ -const metadata_tag equivalent_metadata_tags[] = +const iptc_tag_info equivalent_metadata_tags[] = { - { "Iptc.Application2.DateCreated", MODE_SINGLE, 10, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 0 - { "Iptc.Application2.TransmissionReference", MODE_SINGLE, 12, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 1 - { "Iptc.Application2.SpecialInstructions", MODE_SINGLE, 13, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 2 - { "Iptc.Application2.Headline", MODE_SINGLE, 11, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 3 - { "Iptc.Application2.Category", MODE_SINGLE, 56, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 4 - { "Iptc.Application2.City", MODE_SINGLE, 20, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 5 - { "Iptc.Application2.ProvinceState", MODE_SINGLE, 21, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 6 - { "Iptc.Application2.CountryName", MODE_SINGLE, 22, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 7 - { "Iptc.Application2.Credit", MODE_SINGLE, 24, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 8 - { "Iptc.Application2.Source", MODE_SINGLE, 25, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 9 - { "Iptc.Application2.SuppCategory", MODE_MULTI, 57, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 10 - { "Iptc.Application2.Urgency", MODE_COMBO, 26, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 11 - { "Iptc.Application2.SubLocation", MODE_SINGLE, 28, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 12 - { "Iptc.Application2.Byline", MODE_SINGLE, 1, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 13 - { "Iptc.Application2.Caption", MODE_SINGLE, 2, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 14 - { "Iptc.Application2.Keywords", MODE_MULTI, 3, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 15 - { "Iptc.Application2.ObjectName", MODE_SINGLE, 0, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 16 - { "Iptc.Application2.Copyright", MODE_SINGLE, 4, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 17 - { "Iptc.Application2.LocationName", MODE_MULTI, 16, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 18 - { "Iptc.Application2.BylineTitle", MODE_MULTI, 5, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 19 - { "Iptc.Application2.CountryCode", MODE_SINGLE, 17, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 20 - { "Iptc.Application2.Writer", MODE_MULTI, 6, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 21 + { "Iptc.Application2.DateCreated", MODE_SINGLE, 10, -1 }, // 0 + { "Iptc.Application2.TransmissionReference", MODE_SINGLE, 12, -1 }, // 1 + { "Iptc.Application2.SpecialInstructions", MODE_SINGLE, 13, -1 }, // 2 + { "Iptc.Application2.Headline", MODE_SINGLE, 11, -1 }, // 3 + { "Iptc.Application2.Category", MODE_SINGLE, 56, -1 }, // 4 + { "Iptc.Application2.City", MODE_SINGLE, 20, -1 }, // 5 + { "Iptc.Application2.ProvinceState", MODE_SINGLE, 21, -1 }, // 6 + { "Iptc.Application2.CountryName", MODE_SINGLE, 22, -1 }, // 7 + { "Iptc.Application2.Credit", MODE_SINGLE, 24, -1 }, // 8 + { "Iptc.Application2.Source", MODE_SINGLE, 25, -1 }, // 9 + { "Iptc.Application2.SuppCategory", MODE_MULTI, 57, -1 }, // 10 + { "Iptc.Application2.Urgency", MODE_COMBO, 26, -1 }, // 11 + { "Iptc.Application2.SubLocation", MODE_SINGLE, 28, -1 }, // 12 + { "Iptc.Application2.Byline", MODE_SINGLE, 1, 0 }, // 13 + { "Iptc.Application2.Caption", MODE_SINGLE, 2, 1 }, // 14 + { "Iptc.Application2.Keywords", MODE_MULTI, 3, -1 }, // 15 + { "Iptc.Application2.ObjectName", MODE_SINGLE, 0, -1 }, // 16 + { "Iptc.Application2.Copyright", MODE_SINGLE, 4, 2 }, // 17 + { "Iptc.Application2.LocationName", MODE_MULTI, 16, -1 }, // 18 + { "Iptc.Application2.BylineTitle", MODE_MULTI, 5, -1 }, // 19 + { "Iptc.Application2.CountryCode", MODE_SINGLE, 17, -1 }, // 20 + { "Iptc.Application2.Writer", MODE_MULTI, 6, -1 }, // 21 }; const gint n_equivalent_metadata_tags = G_N_ELEMENTS (equivalent_metadata_tags); +const exif_tag_info exif_equivalent_tags[] = +{ + { 1, "Exif.Image.Artist", MODE_SINGLE}, // 0 + { 2, "Exif.Image.ImageDescription", MODE_SINGLE}, // 1 + { 4, "Exif.Image.Copyright", MODE_SINGLE}, // 2 +}; + /* Digital Source Type Combobox Items * http://cv.iptc.org/newscodes/digitalsourcetype/ */ diff --git a/plug-ins/metadata/metadata-tags.h b/plug-ins/metadata/metadata-tags.h index b9169fcd37..30a4cfc96b 100644 --- a/plug-ins/metadata/metadata-tags.h +++ b/plug-ins/metadata/metadata-tags.h @@ -123,11 +123,13 @@ enum METADATA_SPECIAL_PROCESSING METADATA_PREPROCESS_TEXT }; -extern const metadata_tag default_metadata_tags[]; -extern const gint n_default_metadata_tags; +extern const metadata_tag default_metadata_tags[]; +extern const gint n_default_metadata_tags; -extern const metadata_tag equivalent_metadata_tags[]; -extern const gint n_equivalent_metadata_tags; +extern const iptc_tag_info equivalent_metadata_tags[]; +extern const gint n_equivalent_metadata_tags; + +extern const exif_tag_info exif_equivalent_tags[]; /* Tag indexes in equivalent_metadata_tags that need special processing. */ #define SPECIAL_PROCESSING_DATE_CREATED 0