[gexiv2] Fix get/set Iptc Repeatable tags



commit 8398396cf42bbcce44f0ca4bd08306e1353808c8
Author: postscript-dev <43813-postscript-dev users noreply gitlab gnome org>
Date:   Sat Mar 20 23:28:43 2021 +0000

    Fix get/set Iptc Repeatable tags
    
    Iptc allows repeatable (multiple value) and non-repeatable (single
    value) tags. Repeatable tags are not stored as a single group but as
    separate elements, each with the same tag name. This arrangement
    caused the problems found in issue #62.
    
    Fix closes #62, changes the public interface to provide get/set access in a
    similar way to Xmp array tags.
    
    Changes:
    + Fix the get/set string/multiple/raw versions of the functions
    + Modernize code (e.g. nullptr for NULL)
    + Add Regression test
    + Update the public documentation

 gexiv2/gexiv2-metadata-iptc.cpp | 250 +++++++++++++++++++++++++++++++---------
 gexiv2/gexiv2-metadata.h        |  34 +++++-
 test/gexiv2-regression.c        | 136 ++++++++++++++++++++++
 3 files changed, 359 insertions(+), 61 deletions(-)
---
diff --git a/gexiv2/gexiv2-metadata-iptc.cpp b/gexiv2/gexiv2-metadata-iptc.cpp
index afeec29..356fd7c 100644
--- a/gexiv2/gexiv2-metadata-iptc.cpp
+++ b/gexiv2/gexiv2-metadata-iptc.cpp
@@ -101,64 +101,136 @@ gchar** gexiv2_metadata_get_iptc_tags(GExiv2Metadata* self) {
 }
 
 gchar* gexiv2_metadata_get_iptc_tag_string (GExiv2Metadata *self, const gchar* tag, GError **error) {
-    g_return_val_if_fail(GEXIV2_IS_METADATA (self), NULL);
-    g_return_val_if_fail(tag != NULL, NULL);
-    g_return_val_if_fail(self->priv->image.get() != NULL, NULL);
+    g_return_val_if_fail(GEXIV2_IS_METADATA (self), nullptr);
+    g_return_val_if_fail(tag != nullptr, nullptr);
+    g_return_val_if_fail(self->priv != nullptr, nullptr);
+    g_return_val_if_fail(self->priv->image.get() != nullptr, nullptr);
     g_return_val_if_fail(error == nullptr || *error == nullptr, FALSE);
     
-    Exiv2::IptcData& iptc_data = self->priv->image->iptcData();
-    
     try {
-        Exiv2::IptcData::iterator it = iptc_data.findKey(Exiv2::IptcKey(tag));
+        const auto& iptc_data = self->priv->image->iptcData();
+       const Exiv2::IptcKey key(tag);
+        auto it = iptc_data.findKey(key);
+
         while (it != iptc_data.end() && it->count() == 0)
             it++;
-        
-        if (it != iptc_data.end())
-            return g_strdup (it->toString ().c_str ());
+
+        if (it != iptc_data.end()) {
+               std::ostringstream os;
+
+               // Iptc allows Repeatable tags (multi-value) and Non-Repeatable tags
+               // (single value). Repeatable tags are not grouped together, but exist as
+               // separate entries with the same tag name.
+               if (!Exiv2::IptcDataSets::dataSetRepeatable(key.tag(), key.record())) {
+                return g_strdup (it->toString ().c_str ());
+               }
+            const gchar* SEPARATOR = ", ";
+            gboolean add_separator = FALSE;
+
+            for (; it != iptc_data.end(); ++it) {
+                if (it->key() == tag) {
+                    if (add_separator == TRUE) {
+                       os << SEPARATOR;
+                    }
+                    os << it->toString();
+                    add_separator = TRUE;
+                }
+            }
+            return g_strdup (os.str().c_str());
+        }
     } catch (Exiv2::Error& e) {
         g_set_error_literal (error, g_quark_from_string ("GExiv2"), e.code (), e.what ());
     }
     
-    return NULL;
+    return nullptr;
 }
 
 gchar* gexiv2_metadata_get_iptc_tag_interpreted_string (GExiv2Metadata *self, const gchar* tag, GError 
**error) {
-    g_return_val_if_fail(GEXIV2_IS_METADATA (self), NULL);
-    g_return_val_if_fail(tag != NULL, NULL);
-    g_return_val_if_fail(self->priv->image.get() != NULL, NULL);
+    g_return_val_if_fail(GEXIV2_IS_METADATA (self), nullptr);
+    g_return_val_if_fail(tag != nullptr, nullptr);
+    g_return_val_if_fail(self->priv != nullptr, nullptr);
+    g_return_val_if_fail(self->priv->image.get() != nullptr, nullptr);
     g_return_val_if_fail(error == nullptr || *error == nullptr, FALSE);
     
-    Exiv2::IptcData& iptc_data = self->priv->image->iptcData();
-    
     try {
-        Exiv2::IptcData::iterator it = iptc_data.findKey(Exiv2::IptcKey(tag));
+        const auto& iptc_data = self->priv->image->iptcData();
+        const Exiv2::IptcKey key(tag);
+        auto it = iptc_data.findKey(key);
+
         while (it != iptc_data.end() && it->count() == 0)
             it++;
-        
+
         if (it != iptc_data.end()) {
-            std::ostringstream os;
-            it->write (os);
-            
-            return g_strdup (os.str ().c_str ());
+               std::ostringstream os;
+
+               // Iptc allows Repeatable tags (multi-value) and Non-Repeatable tags
+               // (single value). Repeatable tags are not grouped together, but exist as
+               // separate entries with the same tag name.
+               if (!Exiv2::IptcDataSets::dataSetRepeatable(key.tag(), key.record())) {
+                it->write (os);
+                return g_strdup (os.str().c_str());
+               }
+            const gchar* SEPARATOR = ", ";
+            gboolean add_separator = FALSE;
+
+            for (; it != iptc_data.end(); ++it) {
+                if (it->key() == tag) {
+                    if (add_separator == TRUE) {
+                       os << SEPARATOR;
+                    }
+                    it->write(os);
+                    add_separator = TRUE;
+                }
+            }
+            return g_strdup (os.str().c_str());
         }
     } catch (Exiv2::Error& e) {
         g_set_error_literal (error, g_quark_from_string ("GExiv2"), e.code (), e.what ());
     }
     
-    return NULL;
+    return nullptr;
 }
 
 gboolean gexiv2_metadata_set_iptc_tag_string (GExiv2Metadata *self, const gchar* tag,
     const gchar* value, GError **error) {
     g_return_val_if_fail (GEXIV2_IS_METADATA (self), FALSE);
-    g_return_val_if_fail(tag != NULL, FALSE);
-    g_return_val_if_fail(value != NULL, FALSE);
-    g_return_val_if_fail(self->priv->image.get() != NULL, FALSE);
+    g_return_val_if_fail(tag != nullptr, FALSE);
+    g_return_val_if_fail(value != nullptr, FALSE);
+    g_return_val_if_fail(self->priv != nullptr, FALSE);
+    g_return_val_if_fail(self->priv->image.get() != nullptr, FALSE);
     g_return_val_if_fail(error == nullptr || *error == nullptr, FALSE);
     
     try {
-        self->priv->image->iptcData()[tag] = value;
-        
+        const Exiv2::IptcKey key(tag);
+        auto& iptc_data = self->priv->image->iptcData();
+
+       // Iptc allows Repeatable tags (multi-value) and Non-Repeatable tags
+       // (single value). Repeatable tags are not grouped together, but exist as
+       // separate entries with the same tag name.
+        if (!Exiv2::IptcDataSets::dataSetRepeatable(key.tag(), key.record())) {
+            iptc_data[tag] = value;
+            return TRUE;
+        }
+
+        // Repeatable values can be any type
+               GError** error = nullptr;
+       const gchar* type = gexiv2_metadata_get_iptc_tag_type(tag, error);
+
+        if (error != nullptr && *error != nullptr) {
+            g_set_error_literal (error, g_quark_from_string ("GExiv2"), (*error)->code, (*error)->message);
+            return FALSE;
+        }
+
+        if (type == nullptr)
+            throw Exiv2::Error(Exiv2::ErrorCode::kerInvalidKey, tag);
+
+        auto v = Exiv2::Value::create(Exiv2::TypeInfo::typeId(static_cast<const std::string>(type)));
+
+
+
+        if (v->read(static_cast<const std::string>(value)) != 0 || iptc_data.add(key,v.get()) != 0)
+               return FALSE;
+
         return TRUE;
     } catch (Exiv2::Error& e) {
         g_set_error_literal (error, g_quark_from_string ("GExiv2"), e.code (), e.what ());
@@ -209,17 +281,22 @@ gchar** gexiv2_metadata_get_iptc_tag_multiple (GExiv2Metadata *self, const gchar
 gboolean gexiv2_metadata_set_iptc_tag_multiple (GExiv2Metadata *self, const gchar* tag,
     const gchar** values, GError **error) {
     g_return_val_if_fail (GEXIV2_IS_METADATA (self), FALSE);
-    g_return_val_if_fail(tag != NULL, FALSE);
-    g_return_val_if_fail(values != NULL, FALSE);
-    g_return_val_if_fail(self->priv->image.get() != NULL, FALSE);
+    g_return_val_if_fail(tag != nullptr, FALSE);
+    g_return_val_if_fail(values != nullptr, FALSE);
+    g_return_val_if_fail(self->priv != nullptr, FALSE);
+    g_return_val_if_fail(self->priv->image.get() != nullptr, FALSE);
     g_return_val_if_fail(error == nullptr || *error == nullptr, FALSE);
-    
-    Exiv2::IptcData& iptc_data = self->priv->image->iptcData();
-    
+
     try {
+        auto& iptc_data = self->priv->image->iptcData();
+
+       // Iptc allows Repeatable tags (multi-value) and Non-Repeatable tags
+       // (single value). Repeatable tags are not grouped together, but exist as
+       // separate entries with the same tag name.
+
         /* first clear all existing ... */
-        Exiv2::IptcKey iptc_key(tag);
-        Exiv2::IptcData::iterator iptc_it = iptc_data.begin();
+        const Exiv2::IptcKey iptc_key(tag);
+        auto iptc_it = iptc_data.begin();
         while (iptc_it != iptc_data.end()) {
             if (iptc_it->count() > 0 && iptc_key.key () == iptc_it->key ())
                 iptc_it = iptc_data.erase (iptc_it);
@@ -227,17 +304,43 @@ gboolean gexiv2_metadata_set_iptc_tag_multiple (GExiv2Metadata *self, const gcha
                 ++iptc_it;
         }
         
-        /* ... and then set the others */
-        auto iptc_value = Exiv2::Value::create(Exiv2::string);
-            
-        const gchar **it = values;
-        while (*it != NULL) {
-            iptc_value->read (static_cast<const std::string>(*it));
-            iptc_data.add (iptc_key, iptc_value.get());
-            
-            ++it;
+        if (!Exiv2::IptcDataSets::dataSetRepeatable(iptc_key.tag(), iptc_key.record())) {
+               // Skip to last value and assign
+            const gchar** val_it = values;
+            while (*val_it != nullptr) {
+                val_it++;
+            }
+
+            --val_it;
+            iptc_data[tag] = static_cast<const std::string>(*val_it);
+
+            return TRUE;
         }
-        
+
+        // Repeatable values can be any type
+       GError** error = nullptr;
+       const gchar* type = gexiv2_metadata_get_iptc_tag_type(tag, error);
+
+        if (error != nullptr && *error != nullptr) {
+            g_set_error_literal (error, g_quark_from_string ("GExiv2"), (*error)->code, (*error)->message);
+            return FALSE;
+        }
+
+        if (type == nullptr)
+            throw Exiv2::Error(Exiv2::ErrorCode::kerInvalidKey, tag);
+
+        auto v = Exiv2::Value::create(Exiv2::TypeInfo::typeId(static_cast<const std::string>(type)));
+
+        // Add each value separately
+        const gchar** val_it = values;
+        while (*val_it != nullptr) {
+
+            if (v->read(static_cast<const std::string>(*val_it)) != 0 || iptc_data.add(iptc_key,v.get()) != 
0) {
+               return FALSE;
+            }
+            val_it++;
+        }
+
         return TRUE;
     } catch (Exiv2::Error& e) {
         g_set_error_literal (error, g_quark_from_string ("GExiv2"), e.code (), e.what ());
@@ -303,31 +406,64 @@ gboolean gexiv2_metadata_iptc_tag_supports_multiple_values(const gchar* tag, GEr
 }
 
 GBytes* gexiv2_metadata_get_iptc_tag_raw (GExiv2Metadata *self, const gchar* tag, GError **error) {
-    g_return_val_if_fail(GEXIV2_IS_METADATA (self), NULL);
-    g_return_val_if_fail(tag != NULL, NULL);
-    g_return_val_if_fail(self->priv->image.get() != NULL, NULL);
+    g_return_val_if_fail(GEXIV2_IS_METADATA (self), nullptr);
+    g_return_val_if_fail(tag != nullptr, nullptr);
+    g_return_val_if_fail(self->priv != nullptr, nullptr);
+    g_return_val_if_fail(self->priv->image.get() != nullptr, nullptr);
     g_return_val_if_fail(error == nullptr || *error == nullptr, FALSE);
 
-    Exiv2::IptcData& iptc_data = self->priv->image->iptcData();
-
     try {
-        Exiv2::IptcData::iterator it = iptc_data.findKey(Exiv2::IptcKey(tag));
+        const auto& iptc_data = self->priv->image->iptcData();
+        const Exiv2::IptcKey key(tag);
+        auto it = iptc_data.findKey(key);
+
         while (it != iptc_data.end() && it->count() == 0)
             it++;
 
         if (it != iptc_data.end()) {
-            long size = it->size();
-            if( size > 0 ) {
-                gpointer data = g_malloc(size);
-                it->copy((Exiv2::byte*)data, Exiv2::invalidByteOrder);
-                return g_bytes_new_take(data, size);
+               // Iptc allows Repeatable tags (multi-value) and Non-Repeatable tags
+               // (single value). Repeatable tags are not grouped together, but exist as
+               // separate entries with the same tag name.
+               if (!Exiv2::IptcDataSets::dataSetRepeatable(key.tag(), key.record())) {
+                long size = it->size();
+                if( size > 0 ) {
+                    gpointer data = g_malloc(size);
+                    it->copy(static_cast<Exiv2::byte*>(data), Exiv2::invalidByteOrder);
+                    return g_bytes_new_take(data, size);
+                }
+               }
+
+               // Create return string by extracting raw value from Exiv2 and
+               // adding separators (if needed).
+               std::ostringstream os;
+            long size = 0;
+            gpointer temp_str = nullptr;
+            gboolean add_separator = FALSE;
+            const gchar* SEPARATOR = ", ";
+            const int seperator_length = strlen(SEPARATOR);
+
+            for (; it != iptc_data.end(); ++it) {
+               size = it->size();
+               if (it->key() == tag && size > 0) {
+                       if (add_separator == TRUE) {
+                               os.write(SEPARATOR, seperator_length);
+                       }
+                       temp_str = g_malloc(size);
+                       it->copy(static_cast<Exiv2::byte*>(temp_str), Exiv2::invalidByteOrder);
+                               os.write(static_cast<const char*>(temp_str), size);
+                    g_free(temp_str);
+
+                    add_separator = TRUE;
+                }
             }
+
+            return g_bytes_new_static((os.str().c_str() ), os.str().length());
         }
     } catch (Exiv2::Error& e) {
         g_set_error_literal (error, g_quark_from_string ("GExiv2"), e.code (), e.what ());
     }
 
-    return NULL;
+    return nullptr;
 }
 
 G_END_DECLS
diff --git a/gexiv2/gexiv2-metadata.h b/gexiv2/gexiv2-metadata.h
index 47e56f4..c8b225e 100644
--- a/gexiv2/gexiv2-metadata.h
+++ b/gexiv2/gexiv2-metadata.h
@@ -625,6 +625,8 @@ gint                        gexiv2_metadata_get_pixel_height        (GExiv2Metadata 
*self);
  *
  * The Exiv2 Tag Reference can be found at <ulink url="http://exiv2.org/metadata.html";></ulink>
  *
+ * Tags that support multiple values are returned as a single string, with elements separated by ", ".
+ *
  * Returns: (transfer full) (allow-none): The tag's value as a string
  *
  * Since: 0.12.2
@@ -638,6 +640,9 @@ gchar*                      gexiv2_metadata_try_get_tag_string      (GExiv2Metadata 
*self, const gchar*
  * @value: The value to set or replace the existing value
  * @error: (allow-none): A return location for a #GError or %NULL
  *
+ * If a tag supports multiple values, then @value is added to any existing values. For single
+ * tags, @value replaces the value.
+ *
  * The Exiv2 Tag Reference can be found at <ulink url="http://exiv2.org/metadata.html";></ulink>
  *
  * Returns: TRUE on success
@@ -653,6 +658,8 @@ gboolean            gexiv2_metadata_try_set_tag_string      (GExiv2Metadata *self, const 
gchar*
  *
  * The Exiv2 Tag Reference can be found at <ulink url="http://exiv2.org/metadata.html";></ulink>
  *
+ * Tags that support multiple values are returned as a single string, with elements separated by ", ".
+ *
  * In case of error, a GLib warning will be logged. Use instead
  * gexiv2_metadata_try_get_tag_string() if you want to avoid this and
  * control if and how the error is outputted.
@@ -672,6 +679,9 @@ gchar*                      gexiv2_metadata_get_tag_string          (GExiv2Metadata 
*self, const gchar* tag
  *
  * The Exiv2 Tag Reference can be found at <ulink url="http://exiv2.org/metadata.html";></ulink>
  *
+ * If a tag supports multiple values, then @value is added to any existing values. For single
+ * value tags, @value replaces the value.
+ *
  * In case of error, a GLib warning will be logged. Use instead
  * gexiv2_metadata_try_set_tag_string() if you want to avoid this and
  * control if and how the error is outputted.
@@ -722,6 +732,8 @@ gboolean gexiv2_metadata_set_xmp_tag_struct (GExiv2Metadata *self, const gchar*
  * An interpreted string is one fit for user display.  It may display units or use formatting
  * appropriate to the type of data the tag holds.
  *
+ * Tags that support multiple values are returned as a single string, with elements separated by ", ".
+ *
  * The Exiv2 Tag Reference can be found at <ulink url="http://exiv2.org/metadata.html";></ulink>
  *
  * Returns: (transfer full) (allow-none): The tag's interpreted value as a string
@@ -738,6 +750,8 @@ gchar*                      gexiv2_metadata_try_get_tag_interpreted_string 
(GExiv2Metadata *self, c
  * An interpreted string is one fit for user display.  It may display units or use formatting
  * appropriate to the type of data the tag holds.
  *
+ * Tags that support multiple values are returned as a single string, with elements separated by ", ".
+ *
  * The Exiv2 Tag Reference can be found at <ulink url="http://exiv2.org/metadata.html";></ulink>
  *
  * Returns: (transfer full) (allow-none): The tag's interpreted value as a string
@@ -814,8 +828,8 @@ gboolean            gexiv2_metadata_set_tag_long            (GExiv2Metadata *self, const 
gchar* tag,
  * The Exiv2 Tag Reference can be found at <ulink url="http://exiv2.org/metadata.html";></ulink>
  *
  * Returns: (transfer full) (allow-none) (array zero-terminated=1): The multiple string values of
- * the tag.  Returns NULL if parameters are NULL or @tag does not begin with recognised type of
- * metadata ("Exif.", "Xmp." or "Iptc.").  For a well formed @tag, returns array[0] = NULL if @tag
+ * @tag.  Returns %NULL if parameters are %NULL or @tag does not begin with recognised type of
+ * metadata ("Exif.", "Xmp." or "Iptc.").  For a well formed @tag, returns array[0] = %NULL if @tag
  * is undefined or is not set in the current metadata.
  *
  * Since: 0.12.2
@@ -831,6 +845,10 @@ gchar**                    gexiv2_metadata_try_get_tag_multiple    (GExiv2Metadata 
*self, const gcha
  *
  * The Exiv2 Tag Reference can be found at <ulink url="http://exiv2.org/metadata.html";></ulink>
  *
+ * All previous @tag values are erased. For multiple value tags, each of the non %NULL
+ * entries in @values is stored. For single value tags, only the last non %NULL value
+ * is assigned.
+ *
  * Returns: Boolean success value
  *
  * Since: 0.12.2
@@ -849,8 +867,8 @@ gboolean            gexiv2_metadata_try_set_tag_multiple    (GExiv2Metadata *self, const 
gcha
  * control if and how the error is outputted.
  *
  * Returns: (transfer full) (allow-none) (array zero-terminated=1): The multiple string values of
- * the tag.  Returns NULL if parameters are NULL or @tag does not begin with recognised type of
- * metadata ("Exif.", "Xmp." or "Iptc.").  For a well formed @tag, returns array[0] = NULL if @tag
+ * the tag.  Returns %NULL if parameters are %NULL or @tag does not begin with recognised type of
+ * metadata ("Exif.", "Xmp." or "Iptc.").  For a well formed @tag, returns array[0] = %NULL if @tag
  * is undefined or is not set in the current metadata.
  * (Note: <ulink url="https://gitlab.gnome.org/GNOME/gexiv2/-/issues/61";>xmpText/langAlt bug</ulink>
  *  is fixed in gexiv2_metadata_try_get_tag_multiple())
@@ -868,6 +886,10 @@ gchar**                    gexiv2_metadata_get_tag_multiple        (GExiv2Metadata 
*self, const gchar* t
  *
  * The Exiv2 Tag Reference can be found at <ulink url="http://exiv2.org/metadata.html";></ulink>
  *
+ * All previous @tag values are erased. For multiple value tags, each of the non %NULL
+ * entries in @values is stored. For single value tags, only the last non %NULL value
+ * is assigned.
+ *
  * In case of error, a GLib warning will be logged. Use instead
  * gexiv2_metadata_try_set_tag_multiple() if you want to avoid this and
  * control if and how the error is outputted.
@@ -887,6 +909,8 @@ gboolean            gexiv2_metadata_set_tag_multiple        (GExiv2Metadata *self, const 
gchar* t
  *
  * The Exiv2 Tag Reference can be found at <ulink url="http://exiv2.org/metadata.html";></ulink>
  *
+ * Tags that support multiple values are returned as a single string, with elements separated by ", ".
+ *
  * Returns: (transfer full) (allow-none): The tag's raw value as a byte array
  *
  * Since: 0.12.2
@@ -900,6 +924,8 @@ GBytes*                     gexiv2_metadata_try_get_tag_raw         (GExiv2Metadata 
*self, const gchar* t
  *
  * The Exiv2 Tag Reference can be found at <ulink url="http://exiv2.org/metadata.html";></ulink>
  *
+ * Tags that support multiple values are returned as a single string, with elements separated by ", ".
+ *
  * Returns: (transfer full) (allow-none): The tag's raw value as a byte array
  *
  * Deprecated: 0.12.2: Use gexiv2_metadata_try_get_tag_raw() instead.
diff --git a/test/gexiv2-regression.c b/test/gexiv2-regression.c
index 2cd0599..c9d779f 100644
--- a/test/gexiv2-regression.c
+++ b/test/gexiv2-regression.c
@@ -327,6 +327,141 @@ static void test_ggo_58(void) {
     g_clear_object(&meta);
 }
 
+/* Regression test for https://gitlab.gnome.org/GNOME/gexiv2/issues/62 */
+static void test_ggo_62(void){
+    GExiv2Metadata* meta = NULL;
+    gboolean result = FALSE;
+    gchar* value = NULL;
+    gchar** values = NULL;
+    GBytes *raw_value = NULL;
+    GError* error = NULL;
+    const gchar* IPTC_REPEATABLE_TAG = "Iptc.Application2.Keywords";
+    const gchar* IPTC_NON_REPEATABLE_TAG = "Iptc.Application2.Category";
+
+    // Output tags
+    const gchar* NR_OUTPUT_SINGLE = "NR 2";
+    const gchar* NR_OUTPUT_MULTIPLE[] = {
+               "NR 2",
+                       NULL
+    };
+
+       // == "NR 2"
+    const gchar NR_OUTPUT_RAW[] = {
+        0x4e, 0x52, 0x20, 0x32
+    };
+
+    const gchar* R_OUTPUT_SINGLE = "R 1a, R 1b, R 2";
+    const gchar* R_OUTPUT_MULTIPLE[] = {
+               "R 1a",
+               "R 1b",
+               "R 2",
+                       NULL
+    };
+
+    // == "R 1a, R 1b, R 2"
+    const gchar R_OUTPUT_RAW[] = {
+        0x52, 0x20, 0x31, 0x61, 0x2c, 0x20,
+        0x52, 0x20, 0x31, 0x62, 0x2c, 0x20,
+        0x52, 0x20, 0x32
+    };
+
+
+    //// Setup
+    meta = gexiv2_metadata_new();
+    g_assert_nonnull(meta);
+
+    result = gexiv2_metadata_open_path(meta, SAMPLE_PATH "/no-metadata.jpg", &error);
+    g_assert_no_error(error);
+    g_assert_true(result);
+
+
+    //// Test setting/getting Non-Repeatable Iptc tag
+    // Setting
+    values = g_new(gchar*, 3);
+    values[0] = g_strdup("NR 1a");
+    values[1] = g_strdup("NR 1b");
+    values[2] = NULL;
+    result = gexiv2_metadata_try_set_tag_multiple(meta, IPTC_NON_REPEATABLE_TAG, (const gchar**)values, 
&error);
+    g_assert_no_error(error);
+    g_assert_true(result);
+    g_strfreev (values);
+
+    result = gexiv2_metadata_try_set_tag_string(meta, IPTC_NON_REPEATABLE_TAG, "NR 2", &error);
+    g_assert_no_error(error);
+    g_assert_true(result);
+
+    // Getting
+    value = gexiv2_metadata_try_get_tag_string(meta, IPTC_NON_REPEATABLE_TAG, &error);
+    g_assert_no_error(error);
+    g_assert_nonnull(value);
+    g_assert_cmpstr(value, ==, NR_OUTPUT_SINGLE);
+    g_free (value);
+
+    value = gexiv2_metadata_try_get_tag_interpreted_string(meta, IPTC_NON_REPEATABLE_TAG, &error);
+    g_assert_no_error(error);
+    g_assert_nonnull(value);
+    g_assert_cmpstr(value, ==, NR_OUTPUT_SINGLE);
+    g_free (value);
+
+    values = gexiv2_metadata_try_get_tag_multiple(meta, IPTC_NON_REPEATABLE_TAG, &error);
+    g_assert_no_error(error);
+    g_assert_nonnull(values);
+    g_assert_true (g_strv_equal ((const gchar**)values, NR_OUTPUT_MULTIPLE));
+    g_assert_null(values[1]);
+    g_strfreev (values);
+
+    raw_value = gexiv2_metadata_try_get_tag_raw(meta, IPTC_NON_REPEATABLE_TAG, &error);
+    g_assert_nonnull (raw_value);
+    g_assert_cmpmem (g_bytes_get_data(raw_value, NULL), g_bytes_get_size(raw_value),
+                     NR_OUTPUT_RAW, sizeof(NR_OUTPUT_RAW));
+    g_clear_pointer (&raw_value, g_bytes_unref);
+
+
+    //// Test setting/getting Repeatable Iptc tag
+    // Setting
+    values = g_new(gchar*, 3);
+    values[0] = g_strdup("R 1a");
+    values[1] = g_strdup("R 1b");
+    values[2] = NULL;
+    result = gexiv2_metadata_try_set_tag_multiple(meta, IPTC_REPEATABLE_TAG, (const gchar**)values, &error);
+    g_assert_no_error(error);
+    g_assert_true(result);
+    g_strfreev (values);
+
+    result = gexiv2_metadata_try_set_tag_string(meta, IPTC_REPEATABLE_TAG, "R 2", &error);
+    g_assert_no_error(error);
+    g_assert_true(result);
+
+    // Getting
+    value = gexiv2_metadata_try_get_tag_string(meta, IPTC_REPEATABLE_TAG, &error);
+    g_assert_no_error(error);
+    g_assert_nonnull(value);
+    g_assert_cmpstr(value, ==, R_OUTPUT_SINGLE);
+    g_free (value);
+
+    value = gexiv2_metadata_try_get_tag_interpreted_string(meta, IPTC_REPEATABLE_TAG, &error);
+    g_assert_no_error(error);
+    g_assert_nonnull(value);
+    g_assert_cmpstr(value, ==, R_OUTPUT_SINGLE);
+    g_free (value);
+
+    values = gexiv2_metadata_try_get_tag_multiple(meta, IPTC_REPEATABLE_TAG, &error);
+    g_assert_no_error(error);
+    g_assert_nonnull(values);
+    g_assert_true (g_strv_equal ((const gchar**)values, R_OUTPUT_MULTIPLE));
+    g_strfreev (values);
+
+    raw_value = gexiv2_metadata_try_get_tag_raw(meta, IPTC_REPEATABLE_TAG, &error);
+    g_assert_nonnull (raw_value);
+    g_assert_cmpmem (g_bytes_get_data(raw_value, NULL), g_bytes_get_size(raw_value),
+                     R_OUTPUT_RAW, sizeof(R_OUTPUT_RAW));
+    g_clear_pointer (&raw_value, g_bytes_unref);
+
+
+    //// Cleanup
+    g_clear_object(&meta);
+}
+
 int main(int argc, char *argv[static argc + 1])
 {
     g_test_init(&argc, &argv, NULL);
@@ -341,6 +476,7 @@ int main(int argc, char *argv[static argc + 1])
     g_test_add_func("/bugs/gnome/gitlab/xx", test_ggo_xx);
     g_test_add_func("/bugs/gnome/gitlab/45", test_ggo_45);
     g_test_add_func("/bugs/gnome/gitlab/58", test_ggo_58);
+    g_test_add_func("/bugs/gnome/gitlab/62", test_ggo_62);
 
     return g_test_run();
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]