From dac3b5c4e7cde73a2959a26314a95e76ba2e58bb Mon Sep 17 00:00:00 2001 From: JM Ibanez Date: Thu, 27 Nov 2025 03:08:54 +1100 Subject: [PATCH 1/2] Break compatibility and depend on GExiv2 >= 0.16 GExiv2 0.15 breaks API/ABI compatibility[1] by effectively removing the gexiv_metadata methods that internally handled errors and replacing them with what used to be the *_try variants which expect callers to handle errors themselves. Because those variants have an additional argument **GError, calling the library without passing the last argument triggers a segfault when the library method attempts to deref garbage from the stack or register. Additionally, GExiv2 0.16 removed the actually-deprecated _free() functions (it's been marked deprecated since 0.10.3), with the expectation that callers are already using g_object_unref. However, we can't directly call g_object_unref from the gobject-sys FFI, since that depends on a different type than us. So, add the glue code here to still allow safely freeing GExiv2Metadata and GExiv2PreviewImage. Since we can't define FFIs with optional args, we need to bite the bullet and just add the extra arg in the relevant methods. To make sure this is restricted to only 0.16, enforce this when looking up gexiv2 from pkg-config. --- [1] https://gitlab.gnome.org/GNOME/gexiv2/-/commit/e05fa4a33ead95d56a5a63cec97d8ed6b1c9cecd [2] https://gitlab.gnome.org/GNOME/gexiv2/-/commit/560c9223bf9244823e7d120053d6e87668005623 --- CHANGELOG | 2 +- Cargo.toml | 1 + build.rs | 14 +- examples/preview_properties.rs | 10 +- examples/raw_tag_access.rs | 8 +- examples/xmp_packet_access.rs | 12 +- src/glue.cpp | 22 ++++ src/lib.rs | 232 +++++++++++++++++++++++++-------- src/test.rs | 69 ++++++++-- 9 files changed, 292 insertions(+), 78 deletions(-) create mode 100644 src/glue.cpp diff --git a/CHANGELOG b/CHANGELOG index 29f2523..1c61931 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,6 @@ UNRELEASED * Add support for g_error_free to prevent memory leaks of GErrors. - * Explicitly check for minimum v0.10 of gexiv2 system library. + * Explicitly check for minimum v0.16 of gexiv2 system library. * Add support for gexiv2_shutdown(), to be called from main thread. * Update minimum supported Rust version (MSRV) to 1.92 due to dependency reqs. diff --git a/Cargo.toml b/Cargo.toml index 3d0c697..b592fca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ bitflags = { version = "1.3", optional = true} [build-dependencies] pkg-config = "0.3" +cc = "1.0" [dev-dependencies] tempfile = "3.3" diff --git a/build.rs b/build.rs index 81c0c1b..ba2c02c 100644 --- a/build.rs +++ b/build.rs @@ -15,13 +15,16 @@ //! Confirm gexiv2 library exists on the system. +extern crate cc; + fn main() { + let mut cfg = cc::Build::new(); let system_library = pkg_config::Config::new() - .atleast_version("0.10") + .atleast_version("0.16") .probe("gexiv2") .unwrap_or_else(|e| { eprintln!( - "\nThe gexiv2 library was not found by pkg-config/pkgconf on your system.\n\n\ + "\nThe gexiv2 library (at least version 0.16) was not found by pkg-config/pkgconf on your system.\n\n\ Consult the README.md file for suggestions on how to acquire it." ); panic!("{}", e); @@ -50,5 +53,12 @@ fn main() { if (major, minor) >= (milestone_major, milestone_minor) { println!("cargo:rustc-cfg={}", flag); } + }; + cfg.file("src/glue.cpp"); + + for path in system_library.include_paths { + cfg.include(path); } + + cfg.compile("gexiv2_sys_glue"); } diff --git a/examples/preview_properties.rs b/examples/preview_properties.rs index 6bc934a..fccf5ef 100644 --- a/examples/preview_properties.rs +++ b/examples/preview_properties.rs @@ -9,10 +9,8 @@ extern crate libc; use std::ffi; use std::ptr; - static FILE_PATH: &str = "/YOUR/FILE/PATH/GOES/HERE.jpg"; - fn get_file_metadata(path: &str) -> *mut gexiv2::GExiv2Metadata { let mut err: *mut gexiv2::GError = ptr::null_mut(); let c_str_path = ffi::CString::new(path.as_bytes()).unwrap(); @@ -26,7 +24,6 @@ fn get_file_metadata(path: &str) -> *mut gexiv2::GExiv2Metadata { } } - fn main() { unsafe { let meta = get_file_metadata(FILE_PATH); @@ -39,9 +36,10 @@ fn main() { let mut cur_offset = 0; while !(*all_preview_props.offset(cur_offset)).is_null() { let preview_prop = *all_preview_props.offset(cur_offset); - let mime_type = ffi::CStr::from_ptr( - gexiv2::gexiv2_preview_properties_get_mime_type(preview_prop) - ).to_str(); + let mime_type = ffi::CStr::from_ptr(gexiv2::gexiv2_preview_properties_get_mime_type( + preview_prop, + )) + .to_str(); println!("{:?}", mime_type); cur_offset += 1; } diff --git a/examples/raw_tag_access.rs b/examples/raw_tag_access.rs index 8f39bd7..8dd7d27 100644 --- a/examples/raw_tag_access.rs +++ b/examples/raw_tag_access.rs @@ -12,18 +12,22 @@ mod example { extern crate libc; use std::ffi; + use std::ptr; use std::slice; use std::str; + use gexiv2_sys::GError; + pub fn example() { unsafe { let metadata = crate::open_buf::make_new_metadata(); + let mut err: *mut GError = ptr::null_mut(); let tag = ffi::CString::new("Exif.Image.ImageDescription").unwrap(); let tag_value = ffi::CString::new("Raw Tag Access Example").unwrap(); - gexiv2::gexiv2_metadata_set_tag_string(metadata, tag.as_ptr(), tag_value.as_ptr()); + gexiv2::gexiv2_metadata_set_tag_string(metadata, tag.as_ptr(), tag_value.as_ptr(), &mut err); - let raw_tag_struct = gexiv2::gexiv2_metadata_get_tag_raw(metadata, tag.as_ptr()); + let raw_tag_struct = gexiv2::gexiv2_metadata_get_tag_raw(metadata, tag.as_ptr(), &mut err); let mut raw_tag_buffer_size: usize = 0; let raw_tag_buffer = diff --git a/examples/xmp_packet_access.rs b/examples/xmp_packet_access.rs index 941fcbc..5b33a6d 100644 --- a/examples/xmp_packet_access.rs +++ b/examples/xmp_packet_access.rs @@ -10,23 +10,27 @@ mod example { extern crate gexiv2_sys as gexiv2; extern crate libc; - use std::ffi; + use std::{ffi, ptr}; + + use gexiv2_sys::GError; pub fn example() { unsafe { let metadata = crate::open_buf::make_new_metadata(); + let mut err: *mut GError = ptr::null_mut(); let tag = ffi::CString::new("Xmp.dc.title").unwrap(); let tag_value = ffi::CString::new("Example").unwrap(); - gexiv2::gexiv2_metadata_set_tag_string(metadata, tag.as_ptr(), tag_value.as_ptr()); + gexiv2::gexiv2_metadata_set_tag_string(metadata, tag.as_ptr(), tag_value.as_ptr(), &mut err); gexiv2::gexiv2_metadata_generate_xmp_packet( metadata, (gexiv2::GExiv2XmpFormatFlags::OMIT_PACKET_WRAPPER - | gexiv2::GExiv2XmpFormatFlags::OMIT_ALL_FORMATTING).bits(), + | gexiv2::GExiv2XmpFormatFlags::OMIT_ALL_FORMATTING) + .bits(), 1, ); - let packet = gexiv2::gexiv2_metadata_get_xmp_packet(metadata); + let packet = gexiv2::gexiv2_metadata_get_xmp_packet(metadata, &mut err); println!("{}", ffi::CStr::from_ptr(packet).to_str().unwrap()); gexiv2::gexiv2_metadata_free(metadata); diff --git a/src/glue.cpp b/src/glue.cpp new file mode 100644 index 0000000..f4daa19 --- /dev/null +++ b/src/glue.cpp @@ -0,0 +1,22 @@ +#include +#include + +extern "C" { + +#ifndef gexiv2_metadata_free + void gexiv2_metadata_free (GExiv2Metadata *self) { + g_return_if_fail(GEXIV2_IS_METADATA(self)); + + g_object_unref(self); + } +#endif + +#ifndef gexiv2_preview_image_free + void gexiv2_preview_image_free(GExiv2PreviewImage *self) { + g_return_if_fail(GEXIV2_IS_PREVIEW_IMAGE(self)); + + g_object_unref(self); + } +#endif + +} diff --git a/src/lib.rs b/src/lib.rs index da979aa..389715d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,8 +114,8 @@ extern "C" { pub fn gexiv2_metadata_get_pixel_height(this: *mut GExiv2Metadata) -> c_int; // Tag management. - pub fn gexiv2_metadata_has_tag(this: *mut GExiv2Metadata, tag: *const c_char) -> c_int; - pub fn gexiv2_metadata_clear_tag(this: *mut GExiv2Metadata, tag: *const c_char) -> c_int; + pub fn gexiv2_metadata_has_tag(this: *mut GExiv2Metadata, tag: *const c_char, error: *mut *mut GError) -> c_int; + pub fn gexiv2_metadata_clear_tag(this: *mut GExiv2Metadata, tag: *const c_char, error: *mut *mut GError) -> c_int; pub fn gexiv2_metadata_clear(this: *mut GExiv2Metadata); pub fn gexiv2_metadata_has_exif(this: *mut GExiv2Metadata) -> c_int; pub fn gexiv2_metadata_clear_exif(this: *mut GExiv2Metadata); @@ -128,75 +128,200 @@ extern "C" { pub fn gexiv2_metadata_get_iptc_tags(this: *mut GExiv2Metadata) -> *mut *mut c_char; // Tag data getters/setters. - pub fn gexiv2_metadata_get_tag_string(this: *mut GExiv2Metadata, tag: *const c_char) -> *const c_char; - pub fn gexiv2_metadata_set_tag_string(this: *mut GExiv2Metadata, tag: *const c_char, value: *const c_char) -> c_int; - pub fn gexiv2_metadata_get_tag_interpreted_string(this: *mut GExiv2Metadata, tag: *const c_char) -> *const c_char; - pub fn gexiv2_metadata_get_tag_multiple(this: *mut GExiv2Metadata, tag: *const c_char) -> *mut *mut c_char; - pub fn gexiv2_metadata_set_tag_multiple(this: *mut GExiv2Metadata, tag: *const c_char, values: *mut *const c_char) -> c_int; - pub fn gexiv2_metadata_get_tag_long(this: *mut GExiv2Metadata, tag: *const c_char) -> c_long; - pub fn gexiv2_metadata_set_tag_long(this: *mut GExiv2Metadata, tag: *const c_char, value: c_long) -> c_int; - pub fn gexiv2_metadata_get_exif_tag_rational(this: *mut GExiv2Metadata, tag: *const c_char, nom: *mut c_int, den: *mut c_int) -> c_int; - pub fn gexiv2_metadata_set_exif_tag_rational(this: *mut GExiv2Metadata, tag: *const c_char, nom: c_int, den: c_int) -> c_int; + pub fn gexiv2_metadata_get_tag_string( + this: *mut GExiv2Metadata, + tag: *const c_char, + error: *mut *mut GError, + ) -> *const c_char; + pub fn gexiv2_metadata_set_tag_string( + this: *mut GExiv2Metadata, + tag: *const c_char, + value: *const c_char, + error: *mut *mut GError, + ) -> c_int; + pub fn gexiv2_metadata_get_tag_interpreted_string( + this: *mut GExiv2Metadata, + tag: *const c_char, + error: *mut *mut GError, + ) -> *const c_char; + pub fn gexiv2_metadata_get_tag_multiple( + this: *mut GExiv2Metadata, + tag: *const c_char, + error: *mut *mut GError, + ) -> *mut *mut c_char; + pub fn gexiv2_metadata_set_tag_multiple( + this: *mut GExiv2Metadata, + tag: *const c_char, + values: *mut *const c_char, + error: *mut *mut GError, + ) -> c_int; + pub fn gexiv2_metadata_get_tag_long( + this: *mut GExiv2Metadata, + tag: *const c_char, + error: *mut *mut GError, + ) -> c_long; + pub fn gexiv2_metadata_set_tag_long( + this: *mut GExiv2Metadata, + tag: *const c_char, + value: c_long, + error: *mut *mut GError, + ) -> c_int; + pub fn gexiv2_metadata_get_exif_tag_rational( + this: *mut GExiv2Metadata, + tag: *const c_char, + nom: *mut c_int, + den: *mut c_int, + error: *mut *mut GError, + ) -> c_int; + pub fn gexiv2_metadata_set_exif_tag_rational( + this: *mut GExiv2Metadata, + tag: *const c_char, + nom: c_int, + den: c_int, + error: *mut *mut GError, + ) -> c_int; // Helper & convenience getters/setters. - pub fn gexiv2_metadata_get_orientation(this: *mut GExiv2Metadata) -> Orientation; - pub fn gexiv2_metadata_set_orientation(this: *mut GExiv2Metadata, orientation: Orientation); - pub fn gexiv2_metadata_get_metadata_pixel_width(this: *mut GExiv2Metadata) -> c_int; - pub fn gexiv2_metadata_get_metadata_pixel_height(this: *mut GExiv2Metadata) -> c_int; - pub fn gexiv2_metadata_set_metadata_pixel_width(this: *mut GExiv2Metadata, width: c_int); - pub fn gexiv2_metadata_set_metadata_pixel_height(this: *mut GExiv2Metadata, height: c_int); - pub fn gexiv2_metadata_get_exposure_time(this: *mut GExiv2Metadata, nom: *mut c_int, den: *mut c_int) -> c_int; - pub fn gexiv2_metadata_get_fnumber(this: *mut GExiv2Metadata) -> c_double; - pub fn gexiv2_metadata_get_focal_length(this: *mut GExiv2Metadata) -> c_double; - pub fn gexiv2_metadata_get_iso_speed(this: *mut GExiv2Metadata) -> c_int; - pub fn gexiv2_metadata_get_comment(this: *mut GExiv2Metadata) -> *const c_char; - pub fn gexiv2_metadata_set_comment(this: *mut GExiv2Metadata, comment: *const c_char); + pub fn gexiv2_metadata_get_orientation(this: *mut GExiv2Metadata, error: *mut *mut GError) -> Orientation; + pub fn gexiv2_metadata_set_orientation(this: *mut GExiv2Metadata, orientation: Orientation, error: *mut *mut GError); + pub fn gexiv2_metadata_get_metadata_pixel_width( + this: *mut GExiv2Metadata, + error: *mut *mut GError, + ) -> c_int; + pub fn gexiv2_metadata_get_metadata_pixel_height( + this: *mut GExiv2Metadata, + error: *mut *mut GError, + ) -> c_int; + pub fn gexiv2_metadata_set_metadata_pixel_width( + this: *mut GExiv2Metadata, + width: c_int, + error: *mut *mut GError, + ); + pub fn gexiv2_metadata_set_metadata_pixel_height( + this: *mut GExiv2Metadata, + height: c_int, + error: *mut *mut GError, + ); + pub fn gexiv2_metadata_get_exposure_time( + this: *mut GExiv2Metadata, + nom: *mut c_int, + den: *mut c_int, + error: *mut *mut GError, + ) -> c_int; + pub fn gexiv2_metadata_get_fnumber(this: *mut GExiv2Metadata, error: *mut *mut GError) -> c_double; + pub fn gexiv2_metadata_get_focal_length(this: *mut GExiv2Metadata, error: *mut *mut GError) -> c_double; + pub fn gexiv2_metadata_get_iso_speed(this: *mut GExiv2Metadata, error: *mut *mut GError) -> c_int; + pub fn gexiv2_metadata_get_comment(this: *mut GExiv2Metadata, error: *mut *mut GError) -> *const c_char; + pub fn gexiv2_metadata_set_comment(this: *mut GExiv2Metadata, comment: *const c_char, error: *mut *mut GError); pub fn gexiv2_metadata_clear_comment(this: *mut GExiv2Metadata); // GPS-related functions. - pub fn gexiv2_metadata_get_gps_longitude(this: *mut GExiv2Metadata, longitude: *mut c_double) -> c_int; - pub fn gexiv2_metadata_get_gps_latitude(this: *mut GExiv2Metadata, latitude: *mut c_double) -> c_int; - pub fn gexiv2_metadata_get_gps_altitude(this: *mut GExiv2Metadata, altitude: *mut c_double) -> c_int; - pub fn gexiv2_metadata_get_gps_info(this: *mut GExiv2Metadata, longitude: *mut c_double, latitude: *mut c_double, altitude: *mut c_double) -> c_int; - pub fn gexiv2_metadata_set_gps_info(this: *mut GExiv2Metadata, longitude: c_double, latitude: c_double, altitude: c_double) -> c_int; - pub fn gexiv2_metadata_delete_gps_info(this: *mut GExiv2Metadata); + pub fn gexiv2_metadata_get_gps_longitude( + this: *mut GExiv2Metadata, + error: *mut *mut GError, + ) -> c_double; + pub fn gexiv2_metadata_get_gps_latitude( + this: *mut GExiv2Metadata, + error: *mut *mut GError, + ) -> c_double; + pub fn gexiv2_metadata_get_gps_altitude( + this: *mut GExiv2Metadata, + error: *mut *mut GError, + ) -> c_double; + pub fn gexiv2_metadata_get_gps_info( + this: *mut GExiv2Metadata, + longitude: *mut c_double, + latitude: *mut c_double, + altitude: *mut c_double, + error: *mut *mut GError, + ) -> c_int; + pub fn gexiv2_metadata_set_gps_info( + this: *mut GExiv2Metadata, + longitude: c_double, + latitude: c_double, + altitude: c_double, + error: *mut *mut GError, + ) -> c_int; + pub fn gexiv2_metadata_delete_gps_info(this: *mut GExiv2Metadata, error: *mut *mut GError); // Tag information functions. pub fn gexiv2_metadata_is_exif_tag(tag: *const c_char) -> c_int; pub fn gexiv2_metadata_is_iptc_tag(tag: *const c_char) -> c_int; pub fn gexiv2_metadata_is_xmp_tag(tag: *const c_char) -> c_int; - pub fn gexiv2_metadata_get_tag_label(tag: *const c_char) -> *const c_char; - pub fn gexiv2_metadata_get_tag_description(tag: *const c_char) -> *const c_char; - pub fn gexiv2_metadata_get_tag_type(tag: *const c_char) -> *const c_char; + pub fn gexiv2_metadata_get_tag_label( + tag: *const c_char, + error: *mut *mut GError, + ) -> *const c_char; + pub fn gexiv2_metadata_get_tag_description( + tag: *const c_char, + error: *mut *mut GError, + ) -> *const c_char; + pub fn gexiv2_metadata_get_tag_type( + tag: *const c_char, + error: *mut *mut GError, + ) -> *const c_char; // Exif thumbnail getter/setters. - pub fn gexiv2_metadata_get_exif_thumbnail(this: *mut GExiv2Metadata, buffer: *mut *mut u8, size: *mut c_int) -> c_int; - pub fn gexiv2_metadata_set_exif_thumbnail_from_file(this: *mut GExiv2Metadata, path: *const c_char, error: *mut *mut GError) -> c_int; - pub fn gexiv2_metadata_set_exif_thumbnail_from_buffer(this: *mut GExiv2Metadata, buffer: *const u8, size: c_int); - pub fn gexiv2_metadata_erase_exif_thumbnail(this: *mut GExiv2Metadata); + pub fn gexiv2_metadata_get_exif_thumbnail( + this: *mut GExiv2Metadata, + buffer: *mut *mut u8, + size: *mut c_int, + ) -> c_int; + pub fn gexiv2_metadata_set_exif_thumbnail_from_file( + this: *mut GExiv2Metadata, + path: *const c_char, + error: *mut *mut GError, + ) -> c_int; + pub fn gexiv2_metadata_set_exif_thumbnail_from_buffer( + this: *mut GExiv2Metadata, + buffer: *const u8, + size: c_int, + error: *mut *mut GError, + ); + pub fn gexiv2_metadata_erase_exif_thumbnail(this: *mut GExiv2Metadata, error: *mut *mut GError); // Preview image properties. - pub fn gexiv2_metadata_get_preview_properties(this: *mut GExiv2Metadata) -> *mut *mut GExiv2PreviewProperties; - pub fn gexiv2_preview_properties_get_mime_type(this: *mut GExiv2PreviewProperties) -> *const c_char; - pub fn gexiv2_preview_properties_get_extension(this: *mut GExiv2PreviewProperties) -> *const c_char; + pub fn gexiv2_metadata_get_preview_properties( + this: *mut GExiv2Metadata, + ) -> *mut *mut GExiv2PreviewProperties; + pub fn gexiv2_preview_properties_get_mime_type( + this: *mut GExiv2PreviewProperties, + ) -> *const c_char; + pub fn gexiv2_preview_properties_get_extension( + this: *mut GExiv2PreviewProperties, + ) -> *const c_char; pub fn gexiv2_preview_properties_get_size(this: *mut GExiv2PreviewProperties) -> c_uint; pub fn gexiv2_preview_properties_get_width(this: *mut GExiv2PreviewProperties) -> c_uint; pub fn gexiv2_preview_properties_get_height(this: *mut GExiv2PreviewProperties) -> c_uint; // Preview images. - pub fn gexiv2_metadata_get_preview_image(this: *mut GExiv2Metadata, props: *mut GExiv2PreviewProperties) -> *mut GExiv2PreviewImage; + pub fn gexiv2_metadata_get_preview_image( + this: *mut GExiv2Metadata, + props: *mut GExiv2PreviewProperties, + error: *mut *mut GError, + ) -> *mut GExiv2PreviewImage; pub fn gexiv2_preview_image_free(this: *mut GExiv2PreviewImage); - pub fn gexiv2_preview_image_get_data(this: *mut GExiv2PreviewImage, size: *mut c_uint) -> *const c_uchar; + pub fn gexiv2_preview_image_get_data( + this: *mut GExiv2PreviewImage, + size: *mut c_uint, + ) -> *const c_uchar; pub fn gexiv2_preview_image_get_mime_type(this: *mut GExiv2PreviewImage) -> *const c_char; pub fn gexiv2_preview_image_get_extension(this: *mut GExiv2PreviewImage) -> *const c_char; pub fn gexiv2_preview_image_get_width(this: *mut GExiv2PreviewImage) -> c_uint; pub fn gexiv2_preview_image_get_height(this: *mut GExiv2PreviewImage) -> c_uint; - pub fn gexiv2_preview_image_write_file(this: *mut GExiv2PreviewImage, path: *const c_char) -> c_long; + pub fn gexiv2_preview_image_write_file( + this: *mut GExiv2PreviewImage, + path: *const c_char, + ) -> c_long; // XMP namespace management. - pub fn gexiv2_metadata_register_xmp_namespace(name: *const c_char, prefix: *const c_char) -> c_int; - pub fn gexiv2_metadata_unregister_xmp_namespace(name: *const c_char) -> c_int; - pub fn gexiv2_metadata_unregister_all_xmp_namespaces(); + pub fn gexiv2_metadata_register_xmp_namespace( + name: *const c_char, + prefix: *const c_char, + error: *mut *mut GError, + ) -> c_int; + pub fn gexiv2_metadata_unregister_xmp_namespace(name: *const c_char, error: *mut *mut GError) -> c_int; + pub fn gexiv2_metadata_unregister_all_xmp_namespaces(error: *mut *mut GError); // Logging. pub fn gexiv2_log_get_default_handler() -> GExiv2LogHandler; @@ -207,13 +332,15 @@ extern "C" { pub fn gexiv2_log_use_glib_logging(); } - #[cfg(feature = "raw-tag-access")] extern "C" { - pub fn gexiv2_metadata_get_tag_raw(this: *mut GExiv2Metadata, tag: *const libc::c_char) -> *mut glib::GBytes; + pub fn gexiv2_metadata_get_tag_raw( + this: *mut GExiv2Metadata, + tag: *const libc::c_char, + error: *mut *mut GError, + ) -> *mut glib::GBytes; } - #[cfg(feature = "xmp-packet-access")] #[macro_use] extern crate bitflags; @@ -233,10 +360,13 @@ bitflags! { #[cfg(feature = "xmp-packet-access")] extern "C" { - pub fn gexiv2_metadata_generate_xmp_packet(this: *mut GExiv2Metadata, xmp_format_flags: libc::c_ulong, padding: u32) -> *const c_char; - pub fn gexiv2_metadata_get_xmp_packet(this: *mut GExiv2Metadata) -> *const c_char; + pub fn gexiv2_metadata_generate_xmp_packet( + this: *mut GExiv2Metadata, + xmp_format_flags: libc::c_ulong, + padding: u32, + ) -> *const c_char; + pub fn gexiv2_metadata_get_xmp_packet(this: *mut GExiv2Metadata, error: *mut *mut GError) -> *const c_char; } - #[cfg(test)] mod test; diff --git a/src/test.rs b/src/test.rs index 6515936..92f7a64 100644 --- a/src/test.rs +++ b/src/test.rs @@ -26,7 +26,6 @@ use std::slice; use super::*; - static MINI_JPEG: &[u8] = &[ 255, 216, 255, 219, 00, 43, 00, 03, 02, 02, 02, 02, 02, 03, 02, 02, 02, 03, 03, 03, 03, 04, 06, 04, 04, 04, 04, 04, 08, 06, 06, 05, 06, 09, 08, 10, 10, 09, 08, 09, 09, 10, 12, 15, 12, 10, 11, @@ -55,6 +54,16 @@ unsafe fn make_new_metadata() -> *mut GExiv2Metadata { metadata } +struct Finalizer { + cleanup: F, +} + +impl Drop for Finalizer { + fn drop(&mut self) { + println!("Drop"); + (self.cleanup)(); + } +} #[test] fn initialize() { @@ -70,13 +79,15 @@ fn get_version() { } } - // Image information. #[test] fn metadata_get_supports_exif() { unsafe { let meta = make_new_metadata(); + let _finalizer = Finalizer { + cleanup: || gexiv2_metadata_free(meta), + }; assert_eq!(gexiv2_metadata_get_supports_exif(meta), 1); } } @@ -85,6 +96,9 @@ fn metadata_get_supports_exif() { fn metadata_get_supports_iptc() { unsafe { let meta = make_new_metadata(); + let _finalizer = Finalizer { + cleanup: || gexiv2_metadata_free(meta), + }; assert_eq!(gexiv2_metadata_get_supports_iptc(meta), 1); } } @@ -93,6 +107,9 @@ fn metadata_get_supports_iptc() { fn metadata_get_supports_xmp() { unsafe { let meta = make_new_metadata(); + let _finalizer = Finalizer { + cleanup: || gexiv2_metadata_free(meta), + }; assert_eq!(gexiv2_metadata_get_supports_xmp(meta), 1); } } @@ -101,6 +118,9 @@ fn metadata_get_supports_xmp() { fn metadata_get_mime_type() { unsafe { let meta = make_new_metadata(); + let _finalizer = Finalizer { + cleanup: || gexiv2_metadata_free(meta), + }; let result = gexiv2_metadata_get_mime_type(meta); let result = ffi::CStr::from_ptr(result).to_str().unwrap(); assert_eq!(result, "image/jpeg"); @@ -111,6 +131,9 @@ fn metadata_get_mime_type() { fn metadata_get_pixel_width() { unsafe { let meta = make_new_metadata(); + let _finalizer = Finalizer { + cleanup: || gexiv2_metadata_free(meta), + }; assert_eq!(gexiv2_metadata_get_pixel_width(meta), 1); } } @@ -119,23 +142,30 @@ fn metadata_get_pixel_width() { fn metadata_get_pixel_height() { unsafe { let meta = make_new_metadata(); + let _finalizer = Finalizer { + cleanup: || gexiv2_metadata_free(meta), + }; assert_eq!(gexiv2_metadata_get_pixel_height(meta), 1); } } - // Helper & convenience getters/setters. #[test] fn metadata_set_and_get_metadata_pixel_width() { unsafe { let meta = make_new_metadata(); - gexiv2_metadata_set_metadata_pixel_width(meta, 2); - assert_eq!(gexiv2_metadata_get_metadata_pixel_width(meta), 2); + let _finalizer = Finalizer { + cleanup: || gexiv2_metadata_free(meta), + }; + gexiv2_metadata_set_metadata_pixel_width(meta, 2, ptr::null_mut()); + assert_eq!( + gexiv2_metadata_get_metadata_pixel_width(meta, ptr::null_mut()), + 2 + ); } } - // Tag information functions. #[test] @@ -172,7 +202,7 @@ fn metadata_is_xmp_tag() { fn metadata_get_tag_label() { unsafe { let tag = ffi::CString::new("Exif.Image.ImageDescription").unwrap(); - let result = gexiv2_metadata_get_tag_label(tag.as_ptr()); + let result = gexiv2_metadata_get_tag_label(tag.as_ptr(), ptr::null_mut()); let result = ffi::CStr::from_ptr(result).to_str().unwrap(); assert_eq!(result, "Image Description"); } @@ -182,7 +212,7 @@ fn metadata_get_tag_label() { fn metadata_get_tag_description() { unsafe { let tag = ffi::CString::new("Exif.Image.FillOrder").unwrap(); - let result = gexiv2_metadata_get_tag_description(tag.as_ptr()); + let result = gexiv2_metadata_get_tag_description(tag.as_ptr(), ptr::null_mut()); let result = ffi::CStr::from_ptr(result).to_str().unwrap(); assert_eq!(result, "The logical order of bits within a byte"); } @@ -192,19 +222,22 @@ fn metadata_get_tag_description() { fn metadata_get_tag_type() { unsafe { let tag = ffi::CString::new("Exif.Image.ImageDescription").unwrap(); - let result = gexiv2_metadata_get_tag_type(tag.as_ptr()); + let result = gexiv2_metadata_get_tag_type(tag.as_ptr(), ptr::null_mut()); let result = ffi::CStr::from_ptr(result).to_str().unwrap(); assert_eq!(result, "Ascii"); } } - // Exif thumbnail getter/setters. #[test] fn metadata_get_and_set_exif_thumbnail_from_buffer() { unsafe { let meta = make_new_metadata(); + let mut err: *mut GError = ptr::null_mut(); + let _finalizer = Finalizer { + cleanup: || gexiv2_metadata_free(meta), + }; let mut thumb: *mut u8 = ptr::null_mut(); let mut thumb_size: libc::c_int = 0; assert_eq!(gexiv2_metadata_get_exif_thumbnail(meta, &mut thumb, &mut thumb_size), 1); @@ -213,6 +246,11 @@ fn metadata_get_and_set_exif_thumbnail_from_buffer() { meta, MINI_JPEG.as_ptr(), MINI_JPEG.len() as libc::c_int, + &mut err, + ); + assert_eq!( + gexiv2_metadata_get_exif_thumbnail(meta, &mut thumb, &mut thumb_size), + 1 ); assert_eq!(gexiv2_metadata_get_exif_thumbnail(meta, &mut thumb, &mut thumb_size), 1); assert!(!thumb.is_null()); @@ -224,6 +262,9 @@ fn metadata_get_and_set_exif_thumbnail_from_buffer() { fn metadata_set_exif_thumbnail_from_file() { unsafe { let meta = make_new_metadata(); + let _finalizer = Finalizer { + cleanup: || gexiv2_metadata_free(meta), + }; let tmp_dir = tempfile::tempdir().unwrap(); let tmp_file_path = tmp_dir.path().join("thumb.jpg"); @@ -250,22 +291,26 @@ fn metadata_set_exif_thumbnail_from_file() { fn metadata_erase_exif_thumbnail() { unsafe { let meta = make_new_metadata(); + let mut err: *mut GError = ptr::null_mut(); + let _finalizer = Finalizer { + cleanup: || gexiv2_metadata_free(meta), + }; let mut thumb: *mut u8 = ptr::null_mut(); let mut thumb_size: libc::c_int = 0; gexiv2_metadata_set_exif_thumbnail_from_buffer( meta, MINI_JPEG.as_ptr(), MINI_JPEG.len() as libc::c_int, + &mut err, ); assert_eq!(gexiv2_metadata_get_exif_thumbnail(meta, &mut thumb, &mut thumb_size), 1); assert!(!thumb.is_null()); - gexiv2_metadata_erase_exif_thumbnail(meta); + gexiv2_metadata_erase_exif_thumbnail(meta, &mut err); assert_eq!(gexiv2_metadata_get_exif_thumbnail(meta, &mut thumb, &mut thumb_size), 1); assert!(thumb.is_null()); } } - // Logging. #[test] From b9c88af675d96b87ea3cd28e8187244d550355cc Mon Sep 17 00:00:00 2001 From: JM Ibanez Date: Sun, 5 Jul 2026 06:31:34 +1000 Subject: [PATCH 2/2] GExiv2 0.16 actually declares itself GExiv2-0.16 GExiv2 0.16 actually installs the pkg-config file `gexiv2-0.16.pc` which means that it actually names itself `gexiv2-0.16`. So, probe for it explicitly. --- build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.rs b/build.rs index ba2c02c..cb6f27e 100644 --- a/build.rs +++ b/build.rs @@ -21,7 +21,7 @@ fn main() { let mut cfg = cc::Build::new(); let system_library = pkg_config::Config::new() .atleast_version("0.16") - .probe("gexiv2") + .probe("gexiv2-0.16") .unwrap_or_else(|e| { eprintln!( "\nThe gexiv2 library (at least version 0.16) was not found by pkg-config/pkgconf on your system.\n\n\