// // Copyright (c) 2017 KAMADA Ken'ichi. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. // //! Read, write, re-read, and compare the results. //! //! This test can be also compiled as a command-line program. extern crate exif; use std::env; use std::fs::File; use std::io::{BufReader, Cursor}; use std::path::Path; use exif::experimental::Writer; #[cfg(not(test))] use exif::Error; use exif::{Exif, In, Reader, Tag, Value}; #[test] fn exif_heic() { rwr_compare("tests/exif.heic"); } #[test] fn exif_jpg() { rwr_compare("tests/exif.jpg"); } #[test] fn exif_png() { rwr_compare("tests/exif.png"); } #[test] fn exif_tif() { rwr_compare("tests/exif.tif"); } #[test] fn exif_webp() { rwr_compare("tests/exif.webp"); } fn main() { for path in env::args_os().skip(1) { rwr_compare(&path); } } fn rwr_compare

(path: P) where P: AsRef, { let path = path.as_ref(); // Read. let file = File::open(path).unwrap(); let mut bufreader = BufReader::new(&file); #[cfg(test)] let exif1 = Reader::new().read_from_container(&mut bufreader).unwrap(); #[cfg(not(test))] let exif1 = match Reader::new().read_from_container(&mut bufreader) { Ok(exif) => exif, Err(e) => { println!("{}: {}: Skipped", path.display(), e); return; } }; let strips = get_strips(&exif1, In::PRIMARY); let tn_strips = get_strips(&exif1, In::THUMBNAIL); let tiles = get_tiles(&exif1, In::PRIMARY); let tn_jpeg = get_jpeg(&exif1, In::THUMBNAIL); // Write. let mut writer = Writer::new(); for f in exif1.fields() { writer.push_field(f); } if let Some(ref strips) = strips { writer.set_strips(strips, In::PRIMARY); } if let Some(ref tn_strips) = tn_strips { writer.set_strips(tn_strips, In::THUMBNAIL); } if let Some(ref tiles) = tiles { writer.set_tiles(tiles, In::PRIMARY); } if let Some(ref tn_jpeg) = tn_jpeg { writer.set_jpeg(tn_jpeg, In::THUMBNAIL); } let mut out = Cursor::new(Vec::new()); #[cfg(test)] writer.write(&mut out, exif1.little_endian()).unwrap(); #[cfg(not(test))] match writer.write(&mut out, exif1.little_endian()) { Ok(_) => {} Err(Error::NotSupported(_)) => { println!("{}: Contains unknown type", path.display()); return; } e => e.unwrap(), } let out = out.into_inner(); // Re-read. let exif2 = Reader::new().read_raw(out).unwrap(); // Sort the fields (some files have wrong tag order). let mut fields1 = exif1.fields().collect::>(); fields1.sort_by_key(|f| (f.ifd_num, f.tag)); let mut fields2 = exif2.fields().collect::>(); fields2.sort_by_key(|f| (f.ifd_num, f.tag)); // Compare. assert_eq!(fields1.len(), fields2.len()); for (f1, f2) in fields1.iter().zip(fields2.iter()) { assert_eq!(f1.tag, f2.tag); assert_eq!(f1.ifd_num, f2.ifd_num); match f1.tag { Tag::StripOffsets | Tag::TileOffsets | Tag::JPEGInterchangeFormat => continue, _ => {} } compare_field_value(&f1.value, &f2.value); } assert_eq!(get_strips(&exif2, In::PRIMARY), strips); assert_eq!(get_strips(&exif2, In::THUMBNAIL), tn_strips); assert_eq!(get_tiles(&exif2, In::PRIMARY), tiles); assert_eq!(get_jpeg(&exif2, In::THUMBNAIL), tn_jpeg); } // Compare field values. fn compare_field_value(value1: &Value, value2: &Value) { // The TIFF standard requires that BYTE, SHORT, or LONG values should // be accepted for any unsigned integer field. if let (Some(it1), Some(it2)) = (value1.iter_uint(), value2.iter_uint()) { assert!(it1.eq(it2)); return; } // Compare other fields strictly. match (value1, value2) { (&Value::Ascii(ref v1), &Value::Ascii(ref v2)) => assert_eq!(v1, v2), (&Value::Rational(ref v1), &Value::Rational(ref v2)) => { assert_eq!(v1.len(), v2.len()); for (r1, r2) in v1.iter().zip(v2.iter()) { assert_eq!(r1.num, r2.num); assert_eq!(r1.denom, r2.denom); } } (&Value::SByte(ref v1), &Value::SByte(ref v2)) => assert_eq!(v1, v2), (&Value::Undefined(ref v1, _), &Value::Undefined(ref v2, _)) => assert_eq!(v1, v2), (&Value::SShort(ref v1), &Value::SShort(ref v2)) => assert_eq!(v1, v2), (&Value::SLong(ref v1), &Value::SLong(ref v2)) => assert_eq!(v1, v2), (&Value::SRational(ref v1), &Value::SRational(ref v2)) => { assert_eq!(v1.len(), v2.len()); for (r1, r2) in v1.iter().zip(v2.iter()) { assert_eq!(r1.num, r2.num); assert_eq!(r1.denom, r2.denom); } } (&Value::Float(ref v1), &Value::Float(ref v2)) => assert_eq!(v1, v2), (&Value::Double(ref v1), &Value::Double(ref v2)) => assert_eq!(v1, v2), _ => panic!("{:?} != {:?}", value1, value2), } } fn get_strips>(exif: &Exif, ifd_num: In) -> Option> { let offsets = exif .get_field(Tag::StripOffsets, ifd_num) .and_then(|f| f.value.iter_uint()); let counts = exif .get_field(Tag::StripByteCounts, ifd_num) .and_then(|f| f.value.iter_uint()); let (offsets, counts) = match (offsets, counts) { (Some(offsets), Some(counts)) => (offsets, counts), (None, None) => return None, _ => panic!("inconsistent strip offsets and byte counts"), }; let buf = exif.buf(); assert_eq!(offsets.len(), counts.len()); let strips = offsets .zip(counts) .map(|(ofs, cnt)| &buf[ofs as usize..(ofs + cnt) as usize]) .collect(); Some(strips) } fn get_tiles>(exif: &Exif, ifd_num: In) -> Option> { let offsets = exif .get_field(Tag::TileOffsets, ifd_num) .and_then(|f| f.value.iter_uint()); let counts = exif .get_field(Tag::TileByteCounts, ifd_num) .and_then(|f| f.value.iter_uint()); let (offsets, counts) = match (offsets, counts) { (Some(offsets), Some(counts)) => (offsets, counts), (None, None) => return None, _ => panic!("inconsistent tile offsets and byte counts"), }; assert_eq!(offsets.len(), counts.len()); let buf = exif.buf(); let strips = offsets .zip(counts) .map(|(ofs, cnt)| &buf[ofs as usize..(ofs + cnt) as usize]) .collect(); Some(strips) } fn get_jpeg>(exif: &Exif, ifd_num: In) -> Option<&[u8]> { let offset = exif .get_field(Tag::JPEGInterchangeFormat, ifd_num) .and_then(|f| f.value.get_uint(0)); let len = exif .get_field(Tag::JPEGInterchangeFormatLength, ifd_num) .and_then(|f| f.value.get_uint(0)); let (offset, len) = match (offset, len) { (Some(offset), Some(len)) => (offset as usize, len as usize), (None, None) => return None, _ => panic!("inconsistent JPEG offset and length"), }; let buf = exif.buf(); Some(&buf[offset..offset + len]) }