Skip to content

Commit a97c68f

Browse files
NoxHarmoniumfelixc
authored andcommitted
Add tests and documentation for BMFF support (fixes #48)
- Support for BMFF files (HEIC, HEIF, AVIF, CR3, JXL/bmff) was already available. However, the initialize function needed to get called for it to work. Also the dependencies need to be correct. This should now be clearer in the tests and documentation
1 parent 283d37f commit a97c68f

File tree

6 files changed

+72
-10
lines changed

6 files changed

+72
-10
lines changed

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ gexiv2 is supported from version 0.10 onwards, and Exiv2 from version 0.23.
6060
For full instructions on how to get started with rexiv2, including how to
6161
install the prerequisite dependencies, refer to the [`SETUP`](SETUP.md) file.
6262

63+
Note that if you want BMFF support (e.g. HEIC, HEIF, AVIF, CR3, JXL/bmff files)
64+
you will need an up to date version of the underlying libraries (gexiv2 v0.13.0 and Exiv2 v0.27.4).
65+
You will also need to ensure that your version of Exiv2 has BMFF support enabled.
66+
This is generally enabled by default, but may be switched off in certain distributions
67+
due to licensing issues.
6368

6469
Versioning & History
6570
--------------------

examples/gps.rs

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ extern crate rexiv2;
33
use rexiv2::Metadata;
44

55
fn main() {
6+
rexiv2::initialize().expect("Unable to initialize rexiv2");
7+
68
if let Ok(meta) = Metadata::new_from_path("examples/example.jpg") {
79
if let Some(location) = meta.get_gps_info() {
810
println!("Location: {:?}", location);

examples/timestamp.rs

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ extern crate rexiv2;
33
use rexiv2::Metadata;
44

55
fn main() {
6+
rexiv2::initialize().expect("Unable to initialize rexiv2");
7+
68
if let Ok(meta) = Metadata::new_from_path("examples/example.jpg") {
79
if let Ok(time) = meta.get_tag_string("Exif.Image.DateTime") {
810
println!("Time: {:?}", time);

src/lib.rs

+18-10
Original file line numberDiff line numberDiff line change
@@ -1021,25 +1021,33 @@ pub fn get_tag_type(tag: &str) -> Result<TagType> {
10211021

10221022
/// Initialize gexiv2.
10231023
///
1024-
/// This must be called in a thread-safe fashion before using rexiv2 in
1025-
/// multi-threaded applications.
1024+
/// This must be called in a thread-safe fashion before using rexiv2.
1025+
/// The library may appear to work without calling this, but some
1026+
/// features such as HEIC/BMFF will silently fail, and the underlying
1027+
/// libraries make the assumption that this will be called so it
1028+
/// is safer to do so.
1029+
///
1030+
/// Calling it first thing in the main function should ensure that
1031+
/// it is executed on the main thread and is thread safe. Do not call
1032+
/// it in a threaded or async context (such as in a tokio context).
1033+
///
1034+
/// # See also
1035+
///
1036+
/// Associated Gexiv2 Source Code: https://gitlab.gnome.org/GNOME/gexiv2/-/blob/e4d65b31cd77f28ef248117e161de9d8cc31d712/gexiv2/gexiv2-startup.cpp#L14
1037+
///
1038+
/// # Examples
1039+
///
1040+
/// Simple call at the start of the application
10261041
///
1027-
/// # Example
10281042
/// ```
1029-
/// use std::sync::{Once, ONCE_INIT};
10301043
/// fn main() {
1031-
/// static START: Once = ONCE_INIT;
1032-
///
1033-
/// START.call_once(|| unsafe {
1034-
/// rexiv2::initialize().expect("Unable to initialize rexiv2");
1035-
/// });
1044+
/// rexiv2::initialize().expect("Unable to initialize rexiv2");
10361045
/// }
10371046
/// ```
10381047
pub fn initialize() -> Result<()> {
10391048
unsafe { int_bool_to_result(gexiv2::gexiv2_initialize()) }
10401049
}
10411050

1042-
10431051
// XMP namespace management.
10441052

10451053
/// Add a new XMP namespace for tags to exist under.

tst/main.rs

+45
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,48 @@ extern crate rexiv2;
1717

1818
use std::path::Path;
1919

20+
use std::sync::Once;
21+
22+
static INIT: Once = Once::new();
23+
24+
///
25+
/// Should be called before any test runs. Will ensure that the library is initialized at most once.
26+
/// This would be the equivalent of a "beforeAll" function in other test libraries.
27+
///
28+
/// Future work: At some strange it might be good to work out if this can be done automatically
29+
/// by the test runner. It doesn't seem to be right now with the stock cargo test runner but
30+
/// it might be possible with 3rd party crates.
31+
///
32+
fn setup_test() {
33+
INIT.call_once(|| rexiv2::initialize().expect("Unable to initialize rexiv2"));
34+
}
2035

2136
#[test]
2237
fn new_from_str_path() {
38+
setup_test();
2339
let sample_path = concat!(env!("CARGO_MANIFEST_DIR"), "/tst/sample.png");
2440
let meta = rexiv2::Metadata::new_from_path(sample_path).unwrap();
2541
assert_eq!(meta.get_media_type().unwrap(), rexiv2::MediaType::Png);
2642
}
2743

2844
#[test]
2945
fn new_from_path() {
46+
setup_test();
3047
let sample_path = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/tst/sample.png"));
3148
let meta = rexiv2::Metadata::new_from_path(sample_path).unwrap();
3249
assert_eq!(meta.get_media_type().unwrap(), rexiv2::MediaType::Png);
3350
}
3451

3552
#[test]
3653
fn new_from_buffer() {
54+
setup_test();
3755
let meta = rexiv2::Metadata::new_from_buffer(include_bytes!("sample.png")).unwrap();
3856
assert_eq!(meta.get_media_type().unwrap(), rexiv2::MediaType::Png);
3957
}
4058

4159
#[test]
4260
fn new_from_buffer_error() {
61+
setup_test();
4362
let mut bytes = include_bytes!("sample.png").to_vec();
4463
bytes.swap(0, 1);
4564
let meta_result = rexiv2::Metadata::new_from_buffer(&bytes);
@@ -53,24 +72,49 @@ fn new_from_buffer_error() {
5372

5473
#[test]
5574
fn supports_exif() {
75+
setup_test();
5676
let meta = rexiv2::Metadata::new_from_buffer(include_bytes!("sample.png")).unwrap();
5777
assert_eq!(meta.supports_exif(), true);
5878
}
5979

6080
#[test]
6181
fn supports_iptc() {
82+
setup_test();
6283
let meta = rexiv2::Metadata::new_from_buffer(include_bytes!("sample.png")).unwrap();
6384
assert_eq!(meta.supports_iptc(), true);
6485
}
6586

6687
#[test]
6788
fn supports_xmp() {
89+
setup_test();
6890
let meta = rexiv2::Metadata::new_from_buffer(include_bytes!("sample.png")).unwrap();
6991
assert_eq!(meta.supports_xmp(), true);
7092
}
7193

94+
#[test]
95+
fn supports_bmff() {
96+
setup_test();
97+
// iPhone devices use the HEIC (BMFF) file format which only works properly after gexiv2 has been initialized
98+
// (and the underlying libraries are the right version gexiv2 v0.13.0/Exiv2 v0.27.4)
99+
// I copied a photo off an iPhone and shrunk it down to ensure that reading tags works
100+
101+
let meta = rexiv2::Metadata::new_from_buffer(include_bytes!("sample.HEIC")).unwrap();
102+
let gps = meta.get_gps_info().unwrap();
103+
assert_eq!(gps.latitude as i32, -27);
104+
assert_eq!(gps.longitude as i32, 114);
105+
let phone_model = meta.get_tag_string("Exif.Image.Model").unwrap();
106+
assert_eq!(phone_model, "iPhone XS");
107+
108+
// This seems strange since we can read the above information
109+
// We may be missing a "supports" function for bmff tags, or the functions may be returning incorrectly
110+
assert_eq!(meta.supports_exif(), false);
111+
assert_eq!(meta.supports_iptc(), false);
112+
assert_eq!(meta.supports_xmp(), false);
113+
}
114+
72115
#[test]
73116
fn log_levels() {
117+
setup_test();
74118
assert_eq!(rexiv2::get_log_level(), rexiv2::LogLevel::WARN);
75119
rexiv2::set_log_level(rexiv2::LogLevel::INFO);
76120
assert_eq!(rexiv2::get_log_level(), rexiv2::LogLevel::INFO);
@@ -79,6 +123,7 @@ fn log_levels() {
79123
#[test]
80124
#[cfg(feature = "raw-tag-access")]
81125
fn get_tag_raw() {
126+
setup_test();
82127
let meta = rexiv2::Metadata::new_from_buffer(include_bytes!("sample.png")).unwrap();
83128
meta.set_tag_string("Exif.Image.DateTime", "2020:07:12 11:16:35")
84129
.unwrap();

tst/sample.HEIC

7.34 KB
Binary file not shown.

0 commit comments

Comments
 (0)