[gexiv2/wip/58] Sort all meta-data (except exif) with collation



commit 6ada2e8faebc68678273d9ab230491cc032bb724
Author: Jens Georg <mail jensge org>
Date:   Sun Dec 6 21:16:07 2020 +0100

    Sort all meta-data (except exif) with collation
    
    Something is odd in ExifMetadata, it does not fulfil  the requirements
    for std::sort

 gexiv2/gexiv2-metadata-exif.cpp  | 14 ++++++++------
 gexiv2/gexiv2-metadata-iptc.cpp  |  2 +-
 gexiv2/gexiv2-metadata-private.h | 17 ++++++++++++++++-
 gexiv2/gexiv2-metadata-xmp.cpp   | 41 +---------------------------------------
 gexiv2/gexiv2-metadata.cpp       | 40 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 66 insertions(+), 48 deletions(-)
---
diff --git a/gexiv2/gexiv2-metadata-exif.cpp b/gexiv2/gexiv2-metadata-exif.cpp
index 34d6d11..3b63fd9 100644
--- a/gexiv2/gexiv2-metadata-exif.cpp
+++ b/gexiv2/gexiv2-metadata-exif.cpp
@@ -67,15 +67,17 @@ void gexiv2_metadata_clear_exif (GExiv2Metadata *self) {
     self->priv->image->exifData().clear ();
 }
 
-gchar** gexiv2_metadata_get_exif_tags (GExiv2Metadata *self) {
-    g_return_val_if_fail(GEXIV2_IS_METADATA (self), NULL);
-    g_return_val_if_fail(self->priv->image.get() != NULL, NULL);
-    
+gchar** gexiv2_metadata_get_exif_tags(GExiv2Metadata* self) {
+    g_return_val_if_fail(GEXIV2_IS_METADATA(self), nullptr);
+    g_return_val_if_fail(self->priv->image.get() != nullptr, nullptr);
+
     // get a copy of the ExifData and sort it by tags, preserving sort of original
     Exiv2::ExifData exif_data = Exiv2::ExifData(self->priv->image->exifData());
     exif_data.sortByKey();
-    
-    GSList *list = NULL;
+    // FIXME: Use detail::sortMetadata(exif_data);
+    // Something is not right in ExifData that makes std::sort fail
+
+    GSList* list = nullptr;
     GSList *list_iter;
     gchar** data;
     gint count = 0;
diff --git a/gexiv2/gexiv2-metadata-iptc.cpp b/gexiv2/gexiv2-metadata-iptc.cpp
index 303d61a..4977126 100644
--- a/gexiv2/gexiv2-metadata-iptc.cpp
+++ b/gexiv2/gexiv2-metadata-iptc.cpp
@@ -74,7 +74,7 @@ gchar** gexiv2_metadata_get_iptc_tags(GExiv2Metadata* self) {
 
     // get a copy of the IptcData and sort it by key, preserving the original
     Exiv2::IptcData iptc_data = Exiv2::IptcData(self->priv->image->iptcData());
-    iptc_data.sortByKey();
+    detail::sortMetadata(iptc_data);
 
     GSList* list = nullptr;
     GSList* list_iter = nullptr;
diff --git a/gexiv2/gexiv2-metadata-private.h b/gexiv2/gexiv2-metadata-private.h
index 7675945..e6c96fa 100644
--- a/gexiv2/gexiv2-metadata-private.h
+++ b/gexiv2/gexiv2-metadata-private.h
@@ -10,8 +10,23 @@
 #ifndef GEXIV2_METADATA_PRIVATE_H
 #define GEXIV2_METADATA_PRIVATE_H
 
-#include <gexiv2/gexiv2-metadata.h>
+#include <algorithm>
 #include <exiv2/exiv2.hpp>
+#include <gexiv2/gexiv2-metadata.h>
+
+// Internal C++ functions, outside of G_BEGIN_DECLS
+// FIXME: Do we really need G_BEGIN_DECLS/END_DECLS for internal header?
+
+namespace detail {
+G_GNUC_INTERNAL std::string collate_key(const std::string& string);
+
+template<typename T>
+G_GNUC_INTERNAL void sortMetadata(T& container) {
+    std::sort(container.begin(), container.end(), [](Exiv2::Metadatum& a, Exiv2::Metadatum& b) {
+        return collate_key(a.key()) < collate_key(b.key());
+    });
+}
+}; // namespace detail
 
 G_BEGIN_DECLS
 
diff --git a/gexiv2/gexiv2-metadata-xmp.cpp b/gexiv2/gexiv2-metadata-xmp.cpp
index 0713418..fcc48b4 100644
--- a/gexiv2/gexiv2-metadata-xmp.cpp
+++ b/gexiv2/gexiv2-metadata-xmp.cpp
@@ -99,52 +99,13 @@ gboolean gexiv2_metadata_clear_xmp_tag(GExiv2Metadata *self, const gchar* tag) {
     return erased;
 }
 
-// Port of the NaturalCollate.vala collation key generation to C++
-// Simplified to assume that all XMP keys are ASCII and not UTF-8
-// Original source:
-// https://gitlab.gnome.org/GNOME/shotwell/-/blob/master/src/NaturalCollate.vala
-static std::string collate_key(const std::string& str) {
-    constexpr char SUPERDIGIT = ':';
-    constexpr char COLLATION_SENTINAL[] = "\x01\x01\x01";
-    constexpr char NUM_SENTINEL = 0x2;
-
-    std::stringstream in{str};
-    std::stringstream out{};
-
-    while (not in.eof()) {
-        // As long as there are no digits, we put them from input to output
-        while (not std::isdigit(in.peek()) && not in.eof()) {
-            out << static_cast<char>(in.get());
-        }
-
-        if (not in.eof()) {
-
-            // We read the number (integer only)...
-            uint64_t number;
-            in >> number;
-
-            std::string to_append(std::to_string(number).length(), SUPERDIGIT);
-
-            // ... and append it together with its length in : to the output
-            out << COLLATION_SENTINAL << NUM_SENTINEL << to_append << number;
-        }
-    }
-
-    // Add a sentinal for good measure (no idea, follows the original code)
-    out << NUM_SENTINEL;
-
-    return out.str();
-}
-
 gchar** gexiv2_metadata_get_xmp_tags (GExiv2Metadata *self) {
     g_return_val_if_fail(GEXIV2_IS_METADATA (self), NULL);
     g_return_val_if_fail(self->priv->image.get() != NULL, NULL);
     
     // get a copy of the original XmpData and sort it by key, preserving the original
     Exiv2::XmpData xmp_data = Exiv2::XmpData(self->priv->image->xmpData());
-    std::sort(xmp_data.begin(), xmp_data.end(), [](Exiv2::Xmpdatum& a, Exiv2::Xmpdatum& b) {
-        return collate_key(a.key()) <= collate_key(b.key());
-    });
+    detail::sortMetadata(xmp_data);
 
     GSList *list = NULL;
     GSList *list_iter;
diff --git a/gexiv2/gexiv2-metadata.cpp b/gexiv2/gexiv2-metadata.cpp
index 9338247..f811792 100644
--- a/gexiv2/gexiv2-metadata.cpp
+++ b/gexiv2/gexiv2-metadata.cpp
@@ -238,6 +238,46 @@ private:
 }; // class GioIo
 } // Anonymous namespace
 
+// -----------------------------------------------------------------------------
+// Misc internal helper functions
+
+// Port of the NaturalCollate.vala collation key generation to C++
+// Simplified to assume that all XMP keys are ASCII and not UTF-8
+// Original source:
+// https://gitlab.gnome.org/GNOME/shotwell/-/blob/master/src/NaturalCollate.vala
+std::string detail::collate_key(const std::string& str) {
+    constexpr char SUPERDIGIT = ':';
+    constexpr char COLLATION_SENTINAL[] = "\x01\x01\x01";
+    constexpr char NUM_SENTINEL = 0x2;
+
+    std::stringstream in{str};
+    std::stringstream out{};
+
+    while (not in.eof()) {
+        // As long as there are no digits, we put them from input to output
+        while (not std::isdigit(in.peek()) && not in.eof()) {
+            out << static_cast<char>(in.get());
+        }
+
+        if (not in.eof()) {
+
+            // We read the number (integer only)...
+            uint64_t number;
+            in >> number;
+
+            std::string to_append(std::to_string(number).length(), SUPERDIGIT);
+
+            // ... and append it together with its length in : to the output
+            out << COLLATION_SENTINAL << NUM_SENTINEL << to_append << number;
+        }
+    }
+
+    // Add a sentinal for good measure (no idea, follows the original code)
+    out << NUM_SENTINEL;
+
+    return out.str();
+}
+
 G_BEGIN_DECLS
 
 G_DEFINE_TYPE_WITH_CODE (GExiv2Metadata, gexiv2_metadata, G_TYPE_OBJECT, G_ADD_PRIVATE (GExiv2Metadata));


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