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.
This commit is contained in:
Jacob Boerema 2023-08-14 17:29:44 -04:00
parent 8495dd6eed
commit 40b2412fa4
5 changed files with 142 additions and 28 deletions

View File

@ -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)

View File

@ -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<iptc-tag>\n");
g_string_append (xmldata, "\t\t<tag-name>");
g_string_append (xmldata, equivalent_metadata_tags[i].tag);

View File

@ -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;

View File

@ -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/
*/

View File

@ -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