diff --git a/src/lib.rs b/src/lib.rs index 365cb2b..8d0568e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,8 +47,11 @@ extern crate gexiv2_sys as gexiv2; pub use gexiv2::GExiv2LogLevel as LogLevel; use std::ffi; +use std::ffi::c_void; +use std::io::Cursor; use std::ptr; use std::str; +use gexiv2::ManagedStreamCallbacks; /// A wrapper type for the kinds of errors one might encounter when using the library. #[derive(Debug, PartialEq, Eq)] @@ -106,7 +109,7 @@ pub type Result = std::result::Result; /// An opaque structure that serves as a container for a media file's metadata. #[derive(Debug, PartialEq, Eq)] pub struct Metadata { - raw: *mut gexiv2::GExiv2Metadata, + pub raw: *mut gexiv2::GExiv2Metadata, } /// An opaque structure that serves as a container for a preview image. @@ -372,6 +375,23 @@ impl Metadata { } } + /// Put metadata in a buffer by constructing a stream, writing to it, and then reading the result + pub fn save_to_buffer(&self, image_data: &[u8]) -> Result> { + unsafe { + let mut cursor = Cursor::new(Vec::from(image_data)); + let mut callbacks = ManagedStreamCallbacks::new(&mut cursor as *mut Cursor> as *mut c_void); + let mut err: *mut gexiv2::GError = ptr::null_mut(); + let result = gexiv2::gexiv2_metadata_save_stream(self.raw, &mut callbacks as *mut ManagedStreamCallbacks, &mut err); + if result != 1 { + let err_msg = ffi::CStr::from_ptr((*err).message).to_str(); + return Err(Rexiv2Error::Internal( + err_msg.ok().map(|msg| msg.to_string()), + )); + } + + Ok(cursor.into_inner()) + } + } // Image information. diff --git a/tst/main.rs b/tst/main.rs index f25285a..deb4d77 100644 --- a/tst/main.rs +++ b/tst/main.rs @@ -167,6 +167,21 @@ fn log_levels() { assert_eq!(rexiv2::get_log_level(), rexiv2::LogLevel::INFO); } +#[test] +fn test_simple_save_to_buffer() { + test_setup(); + let data = include_bytes!("sample.png"); + let meta = rexiv2::Metadata::new_from_buffer(data).unwrap(); + let result = meta.save_to_buffer(data); + assert!(result.is_ok()); + let unwrapped_result = result.unwrap(); + assert!(unwrapped_result.len() > 0); + assert_eq!(unwrapped_result.len(), data.len()); + + // these should be equal since we didn't adjust anything + assert!(unwrapped_result == data); +} + #[test] #[cfg(feature = "raw-tag-access")] fn get_tag_raw() {