[gexiv2] Add GIO read-only support to Metadata



commit b1795fe1585351d3086373aaf12dd1c146c44c4d
Author: Jens Georg <mail jensge org>
Date:   Tue Jun 19 13:23:51 2018 +0200

    Add GIO read-only support to Metadata
    
    Partial fix for #15

 gexiv2/gexiv2-metadata.cpp | 194 +++++++++++++++++++++++++++++++++++++++++++++
 gexiv2/gexiv2-metadata.h   |  11 +++
 gexiv2/meson.build         |   6 +-
 meson.build                |   1 +
 test/gexiv2-dump.vala      |   4 +-
 test/meson.build           |   2 +-
 6 files changed, 213 insertions(+), 5 deletions(-)
---
diff --git a/gexiv2/gexiv2-metadata.cpp b/gexiv2/gexiv2-metadata.cpp
index 83ec604..bb8a50f 100644
--- a/gexiv2/gexiv2-metadata.cpp
+++ b/gexiv2/gexiv2-metadata.cpp
@@ -31,6 +31,185 @@ using image_ptr = Exiv2::Image::UniquePtr;
 using image_ptr = Exiv2::Image::AutoPtr;
 #endif
 
+namespace {
+class GioIo : public Exiv2::BasicIo {
+public:
+    GioIo (GInputStream *is)
+        : BasicIo ()
+        , _is (G_INPUT_STREAM (g_object_ref (is)))
+        , _seekable(G_IS_SEEKABLE(_is) ? G_SEEKABLE (_is) : NULL)
+        , _error{nullptr}
+        , _eof{false}
+        {}
+
+    ~GioIo() { g_clear_object (&_is); g_clear_error (&_error); _seekable = NULL;}
+#if defined(_MSC_VER)
+    typedef int64_t seek_offset_t;
+#else
+    typedef long seek_offset_t;
+#endif
+
+    int open() {
+        return 0;
+    }
+
+    int close() {
+        return 0;
+    }
+
+    // Writing is not supported
+    long write(const Exiv2::byte *data, long wcount) { return 0; }
+    long write(BasicIo &src) { return 0; }
+    int putb(Exiv2::byte data) { return EOF; }
+
+
+    Exiv2::DataBuf read(long rcount) {
+        Exiv2::DataBuf b{rcount};
+
+        long bytes_read = this->read(b.pData_, rcount);
+        if (bytes_read > 0 && bytes_read != rcount) {
+            b.reset({b.pData_, bytes_read});
+        }
+
+        return b;
+    }
+
+    long read(Exiv2::byte *buf, long rcount) {
+        GError *error = NULL;
+        gssize result = 0;
+
+        result = g_input_stream_read(_is, reinterpret_cast<void *>(buf), rcount, NULL, &error);
+        if (error != NULL) {
+            g_critical ("Error reading from stream: %d %s", error->code, error->message);
+            g_clear_error (&_error);
+            _error = error;
+
+            throw Exiv2::Error(2);
+            return 0;
+        }
+
+        if (result == 0) {
+            _eof = true;
+        } else {
+            _eof = false;
+        }
+
+        return result;
+    }
+
+    int getb() {
+        Exiv2::byte b;
+        return this->read (&b, 1) == 1 ? b : EOF;
+    }
+
+    void transfer(Exiv2::BasicIo &src) {
+        // Does not seem necessary for Read-only support
+    }
+
+    int seek(seek_offset_t offset, Exiv2::BasicIo::Position position) {
+        if (_seekable != NULL && g_seekable_can_seek (_seekable)) {
+            GSeekType t = G_SEEK_SET;
+            switch (position) {
+                case Exiv2::BasicIo::cur:
+                    t = G_SEEK_CUR;
+                    break;
+                case Exiv2::BasicIo::beg:
+                    t = G_SEEK_SET;
+                    break;
+                case Exiv2::BasicIo::end:
+                    t = G_SEEK_END;
+                    break;
+                default:
+                    g_assert_not_reached ();
+                    break;
+            }
+
+            GError *error = NULL;
+            g_seekable_seek (_seekable, offset, t, NULL, &error);
+            if (error != NULL) {
+                g_clear_error(&_error);
+                g_critical ("Failed to seek: %s", error->message);
+                _error = error;
+
+                return -1;
+            }
+
+            return 0;
+        } else {
+            // Can only seek forward here...
+            if (position != Exiv2::BasicIo::cur) {
+                return -1;
+            }
+
+            GError *error = NULL;
+            g_input_stream_skip (_is, offset, NULL, &error);
+            if (error != NULL) {
+                g_clear_error(&_error);
+                _error = error;
+                g_critical("Failed to seek forward: %s", error->message);
+
+                return -1;
+            }
+
+            return 0;
+        }
+    }
+
+    Exiv2::byte *mmap(bool writable) {
+        return NULL;
+    }
+
+    int munmap() {
+        return 0;
+    }
+
+    long tell() const {
+        if (_seekable != NULL && g_seekable_can_seek (_seekable)) {
+            return static_cast<long>(g_seekable_tell (_seekable));
+        } else {
+            return -1;
+        }
+    }
+
+    size_t size() const {
+        return -1;
+    }
+
+    bool isopen() const {
+        return true;
+    }
+
+    int error() const {
+        return _error == nullptr ? 0 : 1;
+    }
+
+    bool eof() const {
+        return _eof;
+    }
+
+    std::string path() const {
+        return "GIO Wrapper";
+    }
+
+#if EXIV2_TEST_VERSION(0,27,99)
+    Exiv2::BasicIo::UniquePtr temporary() const {
+        return Exiv2::BasicIo::UniquePtr(nullptr);
+    }
+#else
+    Exiv2::BasicIo::AutoPtr temporary() const {
+        return Exiv2::BasicIo::AutoPtr(nullptr);
+    }
+#endif
+
+
+private:
+    GInputStream *_is;
+    GSeekable *_seekable;
+    GError *_error;
+    bool _eof;
+}; // class GioIo
+} // Anonymous namespace
+
 G_BEGIN_DECLS
 
 G_DEFINE_TYPE_WITH_CODE (GExiv2Metadata, gexiv2_metadata, G_TYPE_OBJECT, G_ADD_PRIVATE (GExiv2Metadata));
@@ -205,6 +384,21 @@ gboolean gexiv2_metadata_open_stream (GExiv2Metadata *self, ManagedStreamCallbac
     return FALSE;
 }
 
+gboolean gexiv2_metadata_from_stream(GExiv2Metadata *self, GInputStream *stream, GError **error) {
+    g_return_val_if_fail (GEXIV2_IS_METADATA (self), FALSE);
+
+    try {
+        Exiv2::BasicIo::AutoPtr gio_ptr (new GioIo (stream));
+        self->priv->image = Exiv2::ImageFactory::open (gio_ptr);
+
+        return gexiv2_metadata_open_internal (self, error);
+    } catch (Exiv2::Error &e) {
+        g_set_error_literal (error, g_quark_from_string ("GExiv2"), e.code(), e.what());
+    }
+
+    return FALSE;
+}
+
 // Exiv2 does not today offer a clean way to decode a buffer with only the JFIF APP1 segment,
 // where EXIF lives.  This is a common situation when reading EXIF metadata straight from a
 // camera (i.e. via gPhoto) where accessing the entire JPEG image is inconvenient.
diff --git a/gexiv2/gexiv2-metadata.h b/gexiv2/gexiv2-metadata.h
index 1b21c0e..54e0e91 100644
--- a/gexiv2/gexiv2-metadata.h
+++ b/gexiv2/gexiv2-metadata.h
@@ -252,6 +252,17 @@ gboolean           gexiv2_metadata_open_buf                        (GExiv2Metadata 
*self, const guint8 *data,
  */
 gboolean               gexiv2_metadata_open_stream                     (GExiv2Metadata *self, 
ManagedStreamCallbacks* cb, GError **error);
 
+
+/**
+ * gexiv2_metadata_from_stream:
+ * @self: An instance of #GExiv2Metadata
+ * @stream: A #GInputStream to get meta-data from
+ * @error: (allow-none): A return location for a #GError or %NULL
+ *
+ * Read meta-data from a GIO stream
+ */
+gboolean               gexiv2_metadata_from_stream                     (GExiv2Metadata *self, GInputStream* 
stream, GError **error);
+
 /**
  * gexiv2_metadata_from_app1_segment:
  * @self: An instance of #GExiv2Metadata
diff --git a/gexiv2/meson.build b/gexiv2/meson.build
index 85da997..2ccae82 100644
--- a/gexiv2/meson.build
+++ b/gexiv2/meson.build
@@ -63,7 +63,7 @@ gexiv2 = library('gexiv2',
                  link_args : vflag,
                  include_directories : include_directories('..'),
                  version: '2.0.0',
-                 dependencies : [gobject, exiv2],
+                 dependencies : [gobject, exiv2, gio],
                  install : true)
 
 libgexiv2 = declare_dependency(
@@ -72,7 +72,7 @@ libgexiv2 = declare_dependency(
     dependencies : [gobject, exiv2],
 )
 
-pc_deps = ['glib-2.0', 'gobject-2.0']
+pc_deps = ['glib-2.0', 'gobject-2.0', 'gio-2.0']
 
 pkg.generate(
     description : 'GObject bindings for exiv2',
@@ -102,7 +102,7 @@ if introspection_available
       symbol_prefix : 'gexiv2',
       identifier_prefix : 'GExiv2',
       export_packages : 'gexiv2',
-      includes : ['GObject-2.0'],
+      includes : ['GObject-2.0', 'Gio-2.0'],
       header : 'gexiv2/gexiv2.h',
       install : true)
 
diff --git a/meson.build b/meson.build
index 579905f..a7de975 100644
--- a/meson.build
+++ b/meson.build
@@ -12,6 +12,7 @@ pkg = import('pkgconfig')
 
 exiv2 = dependency('exiv2', version : '>= 0.26')
 gobject = dependency('gobject-2.0', version : '>= 2.38.0')
+gio = dependency('gio-2.0', version : '>= 2.32.0')
 
 gir = find_program('g-ir-scanner', required: false)
 vapigen = find_program('vapigen', required: false)
diff --git a/test/gexiv2-dump.vala b/test/gexiv2-dump.vala
index 7bf6ef3..0dc7724 100644
--- a/test/gexiv2-dump.vala
+++ b/test/gexiv2-dump.vala
@@ -18,7 +18,9 @@ int main(string[] args) {
     foreach (string filename in args[1:args.length]) {
         try {
             GExiv2.Metadata metadata = new GExiv2.Metadata();
-            metadata.open_path(filename);
+//            metadata.open_path(filename);
+            var file = File.new_for_path (filename);
+            metadata.from_stream (file.read ());
             
             dump_tags(metadata, metadata.get_exif_tags());
             dump_tags(metadata, metadata.get_iptc_tags());
diff --git a/test/meson.build b/test/meson.build
index 50593b8..f3cc10a 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -23,7 +23,7 @@ if vapi_available
     executable('gexiv2-dump',
                'gexiv2-dump.vala',
                include_directories : include_directories('..'),
-               dependencies : [gobject, vapi],
+               dependencies : [gobject, vapi, gio],
                link_with : gexiv2)
   endif
 endif


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