[gexiv2] Fix gexiv2_metadata_try_get_tag_multiple() xmpText/langAlt return values



commit 88d83208e9756a26c6a8e33adeea886b561b5bed
Author: postscript-dev <43813-postscript-dev users noreply gitlab gnome org>
Date:   Sat Nov 21 15:38:57 2020 +0000

    Fix gexiv2_metadata_try_get_tag_multiple() xmpText/langAlt return values
    
    Function returns incorrect values when tag is of type xmpText or
    altLang (see Gexiv2 Issue GNOME/gexiv2#61).
    
    Fix changes the return value format and is only implemented for
    recently created gexiv2_metadata_try_get_tag_multiple(). For backwards
    compatibility, the old format is preserved in
    gexiv2_metadata_get_tag_multiple().
    
    Changes:
    * Create gexiv2_metadata_get_xmp_tag_multiple_deprecated() to preserve
    the existing format.  Change gexiv2_metadata_get_tag_multiple() to use
    this function.
    
    * Fix gexiv2_metadata_try_get_xml_tag_multiple() to return correct
    values when tag is of type xmpText and xmpAlt.
    
    * Added documentation to gexiv2-metadata.h explaining returns values.
    
    * Change NULL to nullptr as part of modernisation.

 gexiv2/gexiv2-metadata-private.h |   1 +
 gexiv2/gexiv2-metadata-xmp.cpp   | 114 +++++++++++++++++++++++++++++++++------
 gexiv2/gexiv2-metadata.cpp       |  14 +++--
 gexiv2/gexiv2-metadata.h         |  10 +++-
 4 files changed, 118 insertions(+), 21 deletions(-)
---
diff --git a/gexiv2/gexiv2-metadata-private.h b/gexiv2/gexiv2-metadata-private.h
index e6c96fa..e377c6f 100644
--- a/gexiv2/gexiv2-metadata-private.h
+++ b/gexiv2/gexiv2-metadata-private.h
@@ -83,6 +83,7 @@ G_GNUC_INTERNAL gchar*                        
gexiv2_metadata_get_xmp_tag_interpreted_string (GExiv2M
 G_GNUC_INTERNAL glong                  gexiv2_metadata_get_xmp_tag_long        (GExiv2Metadata *self, const 
gchar* tag);
 G_GNUC_INTERNAL gboolean               gexiv2_metadata_set_xmp_tag_long        (GExiv2Metadata *self, const 
gchar* tag, glong value);
 G_GNUC_INTERNAL gchar**                        gexiv2_metadata_get_xmp_tag_multiple (GExiv2Metadata *self, 
const gchar* tag, GError **error);
+G_GNUC_INTERNAL gchar**                        gexiv2_metadata_get_xmp_tag_multiple_deprecated 
(GExiv2Metadata *self, const gchar* tag, GError **error);
 G_GNUC_INTERNAL gboolean               gexiv2_metadata_set_xmp_tag_multiple (GExiv2Metadata *self, const 
gchar* tag, const gchar** values, GError **error);
 
 G_GNUC_INTERNAL const gchar*   gexiv2_metadata_get_xmp_tag_label               (const gchar* tag);
diff --git a/gexiv2/gexiv2-metadata-xmp.cpp b/gexiv2/gexiv2-metadata-xmp.cpp
index fcc48b4..a1c9236 100644
--- a/gexiv2/gexiv2-metadata-xmp.cpp
+++ b/gexiv2/gexiv2-metadata-xmp.cpp
@@ -275,38 +275,122 @@ gboolean gexiv2_metadata_set_xmp_tag_long (GExiv2Metadata *self, const gchar* ta
     return FALSE;
 }
 
-gchar** gexiv2_metadata_get_xmp_tag_multiple (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(error == nullptr || *error == nullptr, FALSE);
-    
-    Exiv2::XmpData& xmp_data = self->priv->image->xmpData();
-    
+gchar** gexiv2_metadata_get_xmp_tag_multiple(GExiv2Metadata* self, const gchar* tag, GError** error) {
+    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, nullptr);
+
+    gchar** array = nullptr; // Return value
+
     try {
+        Exiv2::XmpData& xmp_data = self->priv->image->xmpData();
+
+        const Exiv2::XmpKey key = Exiv2::XmpKey(tag);
+        auto it = xmp_data.findKey(key);
+
+        while (it != xmp_data.end() && it->count() == 0 && it->key() != key.key())
+            it++;
+
+        if (it != xmp_data.end()) {
+            if (it->typeId() == Exiv2::TypeId::xmpText) {
+                // xmpText tags only have single value
+                array = g_new(gchar*, 2);
+                array[1] = nullptr;
+
+                array[0] = g_strdup(it->toString().c_str());
+            } else if (it->typeId() == Exiv2::TypeId::langAlt) {
+                // For langAlt types, it->count() returns the number of
+                // items but it->toString(i) ONLY returns the default
+                // value (if any) minus the "lang=x-default " prefix.
+                //
+                // Instead use it->toString() and parse the result to
+                // create the return array.
+                // (Issue #61 - https://gitlab.gnome.org/GNOME/gexiv2/-/issues/61)
+
+                auto num_items = it->count();
+
+                if (!num_items) {
+                    // Empty string
+                    array = g_new(gchar*, 2);
+                    array[1] = nullptr;
+                    array[0] = g_strdup("");
+                } else {
+                    const int SEPARATOR = 2; // ", "
+                    const std::string temp = it->toString();
+                    std::string::size_type pos1 = 0;
+                    std::string::size_type pos2 = temp.find(',', pos1);
+
+                    array = g_new(gchar*, num_items + 1);
+                    array[num_items] = nullptr;
+
+                    for (decltype(num_items) i = 0; i < num_items; i++) {
+                        array[i] = g_strdup(temp.substr(pos1, pos2 - pos1).c_str());
+                        pos1 = pos2 + SEPARATOR;
+                        pos2 = temp.find(',', pos1);
+                    }
+                }
+            } else {
+                // For Xmp structures, cycle through all elements and
+                // add to return array
+
+                auto num_items = it->count();
+
+                array = g_new(gchar*, num_items + 1);
+                array[num_items] = nullptr;
+
+                for (decltype(num_items) i = 0; i < num_items; i++)
+                    array[i] = g_strdup(it->toString(i).c_str());
+            }
+            return array;
+        }
+    } catch (Exiv2::Error& e) {
+        if (array) {
+            g_strfreev(array);
+        }
+        g_set_error_literal(error, g_quark_from_string("GExiv2"), e.code(), e.what());
+    }
+
+    array = g_new(gchar*, 1);
+    array[0] = nullptr;
+
+    return array;
+}
+
+gchar** gexiv2_metadata_get_xmp_tag_multiple_deprecated (GExiv2Metadata *self, const gchar* tag, GError 
**error) {
+    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, nullptr);
+
+    try {
+        Exiv2::XmpData& xmp_data = self->priv->image->xmpData();
+
         Exiv2::XmpKey key = Exiv2::XmpKey(tag);
         Exiv2::XmpData::iterator it = xmp_data.findKey(key);
 
         while (it != xmp_data.end() && it->count() == 0 && it->key() != key.key())
             it++;
-        
+
         if (it != xmp_data.end()) {
             auto size = it->count ();
             gchar **array = g_new (gchar*, size + 1);
-            array[size] = NULL;
-            
+            array[size] = nullptr;
+
             for (decltype(size) i = 0; i < size; i++)
                 array[i] = g_strdup (it->toString (i).c_str ());
-            
+
             return array;
         }
     } catch (Exiv2::Error& e) {
         g_set_error_literal (error, g_quark_from_string ("GExiv2"), e.code (), e.what ());
     }
-    
+
     gchar **array = g_new (gchar*, 1);
-    array[0] = NULL;
-    
+    array[0] = nullptr;
+
     return array;
 }
 
diff --git a/gexiv2/gexiv2-metadata.cpp b/gexiv2/gexiv2-metadata.cpp
index f811792..991529d 100644
--- a/gexiv2/gexiv2-metadata.cpp
+++ b/gexiv2/gexiv2-metadata.cpp
@@ -1126,15 +1126,21 @@ gboolean gexiv2_metadata_try_set_tag_multiple(GExiv2Metadata *self, const gchar*
     return FALSE;
 }
 
-gchar** gexiv2_metadata_get_tag_multiple(GExiv2Metadata *self, const gchar* tag) {
-    gchar  **tags  = nullptr;
-    GError  *error = nullptr;
+gchar** gexiv2_metadata_get_tag_multiple(GExiv2Metadata* self, const gchar* tag) {
+    gchar** tags = nullptr;
+    GError* error = nullptr;
 
     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);
 
-    tags = gexiv2_metadata_try_get_tag_multiple(self, tag, &error);
+    if (gexiv2_metadata_is_xmp_tag(tag))
+        tags = gexiv2_metadata_get_xmp_tag_multiple_deprecated(self, tag, &error);
+    else if (gexiv2_metadata_is_exif_tag(tag))
+        tags = gexiv2_metadata_get_exif_tag_multiple(self, tag, &error);
+    else if (gexiv2_metadata_is_iptc_tag(tag))
+        tags = gexiv2_metadata_get_iptc_tag_multiple(self, tag, &error);
 
     if (error) {
         g_warning("%s", error->message);
diff --git a/gexiv2/gexiv2-metadata.h b/gexiv2/gexiv2-metadata.h
index 9b26f4c..dceeb7e 100644
--- a/gexiv2/gexiv2-metadata.h
+++ b/gexiv2/gexiv2-metadata.h
@@ -614,7 +614,9 @@ 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
+ * 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.
  *
  * Since: 0.12.2
  */
@@ -647,7 +649,11 @@ 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
+ * 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())
  *
  * Deprecated: 0.12.2: Use gexiv2_metadata_try_get_tag_multiple() instead.
  */


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