Separate struct Exif from Reader.
This commit is contained in:
parent
ffabb8adc2
commit
1568e3151f
|
@ -42,14 +42,14 @@ fn main() {
|
||||||
|
|
||||||
fn dump_file(path: &Path) -> Result<(), exif::Error> {
|
fn dump_file(path: &Path) -> Result<(), exif::Error> {
|
||||||
let file = File::open(path)?;
|
let file = File::open(path)?;
|
||||||
let reader = exif::Reader::read_from_container(
|
let exif = exif::Reader::new().read_from_container(
|
||||||
&mut BufReader::new(&file))?;
|
&mut BufReader::new(&file))?;
|
||||||
|
|
||||||
println!("{}", path.display());
|
println!("{}", path.display());
|
||||||
for f in reader.fields() {
|
for f in exif.fields() {
|
||||||
println!(" {}/{}: {}",
|
println!(" {}/{}: {}",
|
||||||
f.ifd_num.index(), f.tag,
|
f.ifd_num.index(), f.tag,
|
||||||
f.display_value().with_unit(&reader));
|
f.display_value().with_unit(&exif));
|
||||||
if let exif::Value::Ascii(ref v) = f.value {
|
if let exif::Value::Ascii(ref v) = f.value {
|
||||||
println!(" Ascii({:?})",
|
println!(" Ascii({:?})",
|
||||||
v.iter().map(|x| escape(x)).collect::<Vec<_>>());
|
v.iter().map(|x| escape(x)).collect::<Vec<_>>());
|
||||||
|
|
|
@ -33,7 +33,8 @@ use exif::{DateTime, In, Reader, Value, Tag};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let file = File::open("tests/exif.jpg").unwrap();
|
let file = File::open("tests/exif.jpg").unwrap();
|
||||||
let reader = Reader::new(&mut BufReader::new(&file)).unwrap();
|
let exif = Reader::new().read_from_container(
|
||||||
|
&mut BufReader::new(&file)).unwrap();
|
||||||
|
|
||||||
// To obtain a string representation, `Value::display_as`
|
// To obtain a string representation, `Value::display_as`
|
||||||
// or `Field::display_value` can be used. To display a value with its
|
// or `Field::display_value` can be used. To display a value with its
|
||||||
|
@ -44,15 +45,15 @@ fn main() {
|
||||||
Tag::ImageDescription,
|
Tag::ImageDescription,
|
||||||
Tag::DateTime];
|
Tag::DateTime];
|
||||||
for &tag in tag_list.iter() {
|
for &tag in tag_list.iter() {
|
||||||
if let Some(field) = reader.get_field(tag, In::PRIMARY) {
|
if let Some(field) = exif.get_field(tag, In::PRIMARY) {
|
||||||
println!("{}: {}",
|
println!("{}: {}",
|
||||||
field.tag, field.display_value().with_unit(&reader));
|
field.tag, field.display_value().with_unit(&exif));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// To get unsigned integer value(s) from either of BYTE, SHORT,
|
// To get unsigned integer value(s) from either of BYTE, SHORT,
|
||||||
// or LONG, `Value::get_uint` or `Value::iter_uint` can be used.
|
// or LONG, `Value::get_uint` or `Value::iter_uint` can be used.
|
||||||
if let Some(field) = reader.get_field(Tag::PixelXDimension, In::PRIMARY) {
|
if let Some(field) = exif.get_field(Tag::PixelXDimension, In::PRIMARY) {
|
||||||
if let Some(width) = field.value.get_uint(0) {
|
if let Some(width) = field.value.get_uint(0) {
|
||||||
println!("Valid width of the image is {}.", width);
|
println!("Valid width of the image is {}.", width);
|
||||||
}
|
}
|
||||||
|
@ -60,7 +61,7 @@ fn main() {
|
||||||
|
|
||||||
// To convert a Rational or SRational to an f64, `Rational::to_f64`
|
// To convert a Rational or SRational to an f64, `Rational::to_f64`
|
||||||
// or `SRational::to_f64` can be used.
|
// or `SRational::to_f64` can be used.
|
||||||
if let Some(field) = reader.get_field(Tag::XResolution, In::PRIMARY) {
|
if let Some(field) = exif.get_field(Tag::XResolution, In::PRIMARY) {
|
||||||
match field.value {
|
match field.value {
|
||||||
Value::Rational(ref vec) if !vec.is_empty() =>
|
Value::Rational(ref vec) if !vec.is_empty() =>
|
||||||
println!("X resolution is {}.", vec[0].to_f64()),
|
println!("X resolution is {}.", vec[0].to_f64()),
|
||||||
|
@ -69,7 +70,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// To parse a DateTime-like field, `DateTime::from_ascii` can be used.
|
// To parse a DateTime-like field, `DateTime::from_ascii` can be used.
|
||||||
if let Some(field) = reader.get_field(Tag::DateTime, In::PRIMARY) {
|
if let Some(field) = exif.get_field(Tag::DateTime, In::PRIMARY) {
|
||||||
match field.value {
|
match field.value {
|
||||||
Value::Ascii(ref vec) if !vec.is_empty() => {
|
Value::Ascii(ref vec) if !vec.is_empty() => {
|
||||||
if let Ok(datetime) = DateTime::from_ascii(&vec[0]) {
|
if let Ok(datetime) = DateTime::from_ascii(&vec[0]) {
|
||||||
|
|
22
src/lib.rs
22
src/lib.rs
|
@ -35,11 +35,11 @@
|
||||||
//! ```
|
//! ```
|
||||||
//! for path in &["tests/exif.jpg", "tests/exif.tif"] {
|
//! for path in &["tests/exif.jpg", "tests/exif.tif"] {
|
||||||
//! let file = std::fs::File::open(path).unwrap();
|
//! let file = std::fs::File::open(path).unwrap();
|
||||||
//! let reader = exif::Reader::new(
|
//! let exif = exif::Reader::new().read_from_container(
|
||||||
//! &mut std::io::BufReader::new(&file)).unwrap();
|
//! &mut std::io::BufReader::new(&file)).unwrap();
|
||||||
//! for f in reader.fields() {
|
//! for f in exif.fields() {
|
||||||
//! println!("{} {} {}",
|
//! println!("{} {} {}",
|
||||||
//! f.tag, f.ifd_num, f.display_value().with_unit(&reader));
|
//! f.tag, f.ifd_num, f.display_value().with_unit(&exif));
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
//! reader.get_field(Tag::DateTime, false)
|
//! reader.get_field(Tag::DateTime, false)
|
||||||
//! ```
|
//! ```
|
||||||
//! The new code in 0.4.x:
|
//! The new code in 0.4.x:
|
||||||
//! ```
|
//! ```ignore
|
||||||
//! # use exif::{In, Reader, Tag};
|
//! # use exif::{In, Reader, Tag};
|
||||||
//! # let file = std::fs::File::open("tests/exif.tif").unwrap();
|
//! # let file = std::fs::File::open("tests/exif.tif").unwrap();
|
||||||
//! # let reader = Reader::new(
|
//! # let reader = Reader::new(
|
||||||
|
@ -64,7 +64,7 @@
|
||||||
//! ```
|
//! ```
|
||||||
//! As an additional feature, access to the 2nd or further IFD,
|
//! As an additional feature, access to the 2nd or further IFD,
|
||||||
//! which some RAW formats may have, is also possible in 0.4.x:
|
//! which some RAW formats may have, is also possible in 0.4.x:
|
||||||
//! ```
|
//! ```ignore
|
||||||
//! # use exif::{In, Reader, Tag};
|
//! # use exif::{In, Reader, Tag};
|
||||||
//! # let file = std::fs::File::open("tests/exif.tif").unwrap();
|
//! # let file = std::fs::File::open("tests/exif.tif").unwrap();
|
||||||
//! # let reader = Reader::new(
|
//! # let reader = Reader::new(
|
||||||
|
@ -84,9 +84,9 @@
|
||||||
//! ```
|
//! ```
|
||||||
//! # use exif::{In, Reader};
|
//! # use exif::{In, Reader};
|
||||||
//! # let file = std::fs::File::open("tests/exif.tif").unwrap();
|
//! # let file = std::fs::File::open("tests/exif.tif").unwrap();
|
||||||
//! # let reader = Reader::new(
|
//! # let exif = Reader::new().read_from_container(
|
||||||
//! # &mut std::io::BufReader::new(&file)).unwrap();
|
//! # &mut std::io::BufReader::new(&file)).unwrap();
|
||||||
//! # let field = reader.fields().next().unwrap();
|
//! # let field = exif.fields().next().unwrap();
|
||||||
//! match field.ifd_num {
|
//! match field.ifd_num {
|
||||||
//! In::PRIMARY => {}, // for the primary image
|
//! In::PRIMARY => {}, // for the primary image
|
||||||
//! In::THUMBNAIL => {}, // for the thumbnail image
|
//! In::THUMBNAIL => {}, // for the thumbnail image
|
||||||
|
@ -105,14 +105,14 @@
|
||||||
//! It is usually handier than `Value::display_as`.
|
//! It is usually handier than `Value::display_as`.
|
||||||
//! ```
|
//! ```
|
||||||
//! # let file = std::fs::File::open("tests/exif.tif").unwrap();
|
//! # let file = std::fs::File::open("tests/exif.tif").unwrap();
|
||||||
//! # let reader = exif::Reader::new(
|
//! # let exif = exif::Reader::new().read_from_container(
|
||||||
//! # &mut std::io::BufReader::new(&file)).unwrap();
|
//! # &mut std::io::BufReader::new(&file)).unwrap();
|
||||||
//! # let field = reader.fields().next().unwrap();
|
//! # let field = exif.fields().next().unwrap();
|
||||||
//! assert_eq!(field.display_value().to_string(),
|
//! assert_eq!(field.display_value().to_string(),
|
||||||
//! field.value.display_as(field.tag).to_string());
|
//! field.value.display_as(field.tag).to_string());
|
||||||
//! ```
|
//! ```
|
||||||
//! * Displaying a value with its unit is supported.
|
//! * Displaying a value with its unit is supported.
|
||||||
//! ```
|
//! ```ignore
|
||||||
//! # let file = std::fs::File::open("tests/exif.tif").unwrap();
|
//! # let file = std::fs::File::open("tests/exif.tif").unwrap();
|
||||||
//! # let reader = exif::Reader::new(
|
//! # let reader = exif::Reader::new(
|
||||||
//! # &mut std::io::BufReader::new(&file)).unwrap();
|
//! # &mut std::io::BufReader::new(&file)).unwrap();
|
||||||
|
@ -129,7 +129,7 @@
|
||||||
|
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
pub use jpeg::get_exif_attr as get_exif_attr_from_jpeg;
|
pub use jpeg::get_exif_attr as get_exif_attr_from_jpeg;
|
||||||
pub use reader::Reader;
|
pub use reader::{Exif, Reader};
|
||||||
pub use tag::{Context, Tag};
|
pub use tag::{Context, Tag};
|
||||||
pub use tiff::{DateTime, Field, In};
|
pub use tiff::{DateTime, Field, In};
|
||||||
pub use tiff::parse_exif_compat03 as parse_exif;
|
pub use tiff::parse_exif_compat03 as parse_exif;
|
||||||
|
|
119
src/reader.rs
119
src/reader.rs
|
@ -35,53 +35,37 @@ use crate::tag::Tag;
|
||||||
use crate::tiff;
|
use crate::tiff;
|
||||||
use crate::tiff::{Field, IfdEntry, In, ProvideUnit};
|
use crate::tiff::{Field, IfdEntry, In, ProvideUnit};
|
||||||
|
|
||||||
/// The `Reader` struct reads a JPEG or TIFF image,
|
/// The `Reader` struct parses the Exif attributes and
|
||||||
/// parses the Exif attributes in it, and holds the results.
|
/// returns `Exif` struct that holds the results.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// use exif::{In, Reader, Tag};
|
/// use exif::{In, Reader, Tag};
|
||||||
/// let file = std::fs::File::open("tests/exif.jpg").unwrap();
|
/// let file = std::fs::File::open("tests/exif.jpg").unwrap();
|
||||||
/// let reader = Reader::new(&mut std::io::BufReader::new(&file)).unwrap();
|
/// let exif = Reader::new().read_from_container(
|
||||||
/// let xres = reader.get_field(Tag::XResolution, In::PRIMARY).unwrap();
|
/// &mut std::io::BufReader::new(&file)).unwrap();
|
||||||
/// assert_eq!(xres.display_value().with_unit(&reader).to_string(),
|
/// let xres = exif.get_field(Tag::XResolution, In::PRIMARY).unwrap();
|
||||||
|
/// assert_eq!(xres.display_value().with_unit(&exif).to_string(),
|
||||||
/// "72 pixels per inch");
|
/// "72 pixels per inch");
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Reader {
|
pub struct Reader {
|
||||||
// TIFF data.
|
|
||||||
buf: Vec<u8>,
|
|
||||||
// Exif fields. Vec is used to keep the ability to enumerate all fields
|
|
||||||
// even if there are duplicates.
|
|
||||||
entries: Vec<IfdEntry>,
|
|
||||||
// HashMap to the index of the Vec for faster random access.
|
|
||||||
entry_map: HashMap<(In, Tag), usize>,
|
|
||||||
// True if the TIFF data is little endian.
|
|
||||||
little_endian: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Reader {
|
impl Reader {
|
||||||
/// Reads a JPEG or TIFF image and parses the Exif attributes in it.
|
/// Construct a new `Reader`.
|
||||||
/// If an error occurred, `exif::Error` is returned.
|
pub fn new() -> Self {
|
||||||
pub fn new<R>(reader: &mut R)
|
Self {}
|
||||||
-> Result<Reader, Error> where R: io::BufRead {
|
|
||||||
// Parse the data.
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
reader.by_ref().take(4).read_to_end(&mut buf)?;
|
|
||||||
if jpeg::is_jpeg(&buf) {
|
|
||||||
let exif_buf = jpeg::get_exif_attr(
|
|
||||||
&mut buf.as_mut_slice().chain(reader))?;
|
|
||||||
buf = exif_buf;
|
|
||||||
} else if tiff::is_tiff(&buf) {
|
|
||||||
reader.read_to_end(&mut buf)?;
|
|
||||||
} else {
|
|
||||||
return Err(Error::InvalidFormat("Unknown image format"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses the Exif attributes from raw Exif data.
|
||||||
|
/// If an error occurred, `exif::Error` is returned.
|
||||||
|
pub fn read_raw(&self, data: Vec<u8>) -> Result<Exif, Error> {
|
||||||
|
let buf = data;
|
||||||
let (entries, le) = tiff::parse_exif(&buf)?;
|
let (entries, le) = tiff::parse_exif(&buf)?;
|
||||||
let entry_map = entries.iter().enumerate()
|
let entry_map = entries.iter().enumerate()
|
||||||
.map(|(i, e)| (e.ifd_num_tag(), i)).collect();
|
.map(|(i, e)| (e.ifd_num_tag(), i)).collect();
|
||||||
|
|
||||||
Ok(Reader {
|
Ok(Exif {
|
||||||
buf: buf,
|
buf: buf,
|
||||||
entries: entries,
|
entries: entries,
|
||||||
entry_map: entry_map,
|
entry_map: entry_map,
|
||||||
|
@ -96,7 +80,7 @@ impl Reader {
|
||||||
///
|
///
|
||||||
/// This method is provided for the convenience even though
|
/// This method is provided for the convenience even though
|
||||||
/// parsing containers is basically out of the scope of this library.
|
/// parsing containers is basically out of the scope of this library.
|
||||||
pub fn read_from_container<R>(reader: &mut R) -> Result<Reader, Error>
|
pub fn read_from_container<R>(&self, reader: &mut R) -> Result<Exif, Error>
|
||||||
where R: io::BufRead + io::Seek {
|
where R: io::BufRead + io::Seek {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
reader.by_ref().take(4096).read_to_end(&mut buf)?;
|
reader.by_ref().take(4096).read_to_end(&mut buf)?;
|
||||||
|
@ -115,14 +99,45 @@ impl Reader {
|
||||||
let entry_map = entries.iter().enumerate()
|
let entry_map = entries.iter().enumerate()
|
||||||
.map(|(i, e)| (e.ifd_num_tag(), i)).collect();
|
.map(|(i, e)| (e.ifd_num_tag(), i)).collect();
|
||||||
|
|
||||||
Ok(Reader {
|
Ok(Exif {
|
||||||
buf: buf,
|
buf: buf,
|
||||||
entries: entries,
|
entries: entries,
|
||||||
entry_map: entry_map,
|
entry_map: entry_map,
|
||||||
little_endian: le,
|
little_endian: le,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `Exif` struct holds the parsed Exif attributes.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use exif::{In, Reader, Tag};
|
||||||
|
/// # let file = std::fs::File::open("tests/exif.jpg").unwrap();
|
||||||
|
/// # let exif = Reader::new().read_from_container(
|
||||||
|
/// # &mut std::io::BufReader::new(&file)).unwrap();
|
||||||
|
/// // Get a specific field.
|
||||||
|
/// let xres = exif.get_field(Tag::XResolution, In::PRIMARY).unwrap();
|
||||||
|
/// assert_eq!(xres.display_value().with_unit(&exif).to_string(),
|
||||||
|
/// "72 pixels per inch");
|
||||||
|
/// // Iterate over all fields.
|
||||||
|
/// for f in exif.fields() {
|
||||||
|
/// println!("{} {} {}", f.tag, f.ifd_num, f.display_value());
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub struct Exif {
|
||||||
|
// TIFF data.
|
||||||
|
buf: Vec<u8>,
|
||||||
|
// Exif fields. Vec is used to keep the ability to enumerate all fields
|
||||||
|
// even if there are duplicates.
|
||||||
|
entries: Vec<IfdEntry>,
|
||||||
|
// HashMap to the index of the Vec for faster random access.
|
||||||
|
entry_map: HashMap<(In, Tag), usize>,
|
||||||
|
// True if the TIFF data is little endian.
|
||||||
|
little_endian: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Exif {
|
||||||
/// Returns the slice that contains the TIFF data.
|
/// Returns the slice that contains the TIFF data.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn buf(&self) -> &[u8] {
|
pub fn buf(&self) -> &[u8] {
|
||||||
|
@ -151,7 +166,7 @@ impl Reader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ProvideUnit<'a> for &'a Reader {
|
impl<'a> ProvideUnit<'a> for &'a Exif {
|
||||||
fn get_field(self, tag: Tag, ifd_num: In) -> Option<&'a Field> {
|
fn get_field(self, tag: Tag, ifd_num: In) -> Option<&'a Field> {
|
||||||
self.get_field(tag, ifd_num)
|
self.get_field(tag, ifd_num)
|
||||||
}
|
}
|
||||||
|
@ -167,16 +182,17 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn get_field() {
|
fn get_field() {
|
||||||
let file = File::open("tests/yaminabe.tif").unwrap();
|
let file = File::open("tests/yaminabe.tif").unwrap();
|
||||||
let reader = Reader::new(&mut BufReader::new(&file)).unwrap();
|
let exif = Reader::new().read_from_container(
|
||||||
match reader.get_field(Tag::ImageDescription, In(0)).unwrap().value {
|
&mut BufReader::new(&file)).unwrap();
|
||||||
|
match exif.get_field(Tag::ImageDescription, In(0)).unwrap().value {
|
||||||
Value::Ascii(ref vec) => assert_eq!(vec, &[b"Test image"]),
|
Value::Ascii(ref vec) => assert_eq!(vec, &[b"Test image"]),
|
||||||
ref v => panic!("wrong variant {:?}", v)
|
ref v => panic!("wrong variant {:?}", v)
|
||||||
}
|
}
|
||||||
match reader.get_field(Tag::ImageDescription, In(1)).unwrap().value {
|
match exif.get_field(Tag::ImageDescription, In(1)).unwrap().value {
|
||||||
Value::Ascii(ref vec) => assert_eq!(vec, &[b"Test thumbnail"]),
|
Value::Ascii(ref vec) => assert_eq!(vec, &[b"Test thumbnail"]),
|
||||||
ref v => panic!("wrong variant {:?}", v)
|
ref v => panic!("wrong variant {:?}", v)
|
||||||
}
|
}
|
||||||
match reader.get_field(Tag::ImageDescription, In(2)).unwrap().value {
|
match exif.get_field(Tag::ImageDescription, In(2)).unwrap().value {
|
||||||
Value::Ascii(ref vec) => assert_eq!(vec, &[b"Test 2nd IFD"]),
|
Value::Ascii(ref vec) => assert_eq!(vec, &[b"Test 2nd IFD"]),
|
||||||
ref v => panic!("wrong variant {:?}", v)
|
ref v => panic!("wrong variant {:?}", v)
|
||||||
}
|
}
|
||||||
|
@ -185,36 +201,37 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn display_value_with_unit() {
|
fn display_value_with_unit() {
|
||||||
let file = File::open("tests/yaminabe.tif").unwrap();
|
let file = File::open("tests/yaminabe.tif").unwrap();
|
||||||
let reader = Reader::new(&mut BufReader::new(&file)).unwrap();
|
let exif = Reader::new().read_from_container(
|
||||||
|
&mut BufReader::new(&file)).unwrap();
|
||||||
// No unit.
|
// No unit.
|
||||||
let exifver = reader.get_field(Tag::ExifVersion, In::PRIMARY).unwrap();
|
let exifver = exif.get_field(Tag::ExifVersion, In::PRIMARY).unwrap();
|
||||||
assert_eq!(exifver.display_value().with_unit(&reader).to_string(),
|
assert_eq!(exifver.display_value().with_unit(&exif).to_string(),
|
||||||
"2.31");
|
"2.31");
|
||||||
// Fixed string.
|
// Fixed string.
|
||||||
let width = reader.get_field(Tag::ImageWidth, In::PRIMARY).unwrap();
|
let width = exif.get_field(Tag::ImageWidth, In::PRIMARY).unwrap();
|
||||||
assert_eq!(width.display_value().with_unit(&reader).to_string(),
|
assert_eq!(width.display_value().with_unit(&exif).to_string(),
|
||||||
"17 pixels");
|
"17 pixels");
|
||||||
// Unit tag (with a non-default value).
|
// Unit tag (with a non-default value).
|
||||||
let gpsalt = reader.get_field(Tag::GPSAltitude, In::PRIMARY).unwrap();
|
let gpsalt = exif.get_field(Tag::GPSAltitude, In::PRIMARY).unwrap();
|
||||||
assert_eq!(gpsalt.display_value().with_unit(&reader).to_string(),
|
assert_eq!(gpsalt.display_value().with_unit(&exif).to_string(),
|
||||||
"0.5 meters below sea level");
|
"0.5 meters below sea level");
|
||||||
// Unit tag is missing but the default is specified.
|
// Unit tag is missing but the default is specified.
|
||||||
let xres = reader.get_field(Tag::XResolution, In::PRIMARY).unwrap();
|
let xres = exif.get_field(Tag::XResolution, In::PRIMARY).unwrap();
|
||||||
assert_eq!(xres.display_value().with_unit(&reader).to_string(),
|
assert_eq!(xres.display_value().with_unit(&exif).to_string(),
|
||||||
"72 pixels per inch");
|
"72 pixels per inch");
|
||||||
// Unit tag is missing and the default is not specified.
|
// Unit tag is missing and the default is not specified.
|
||||||
let gpslat = reader.get_field(Tag::GPSLatitude, In::PRIMARY).unwrap();
|
let gpslat = exif.get_field(Tag::GPSLatitude, In::PRIMARY).unwrap();
|
||||||
assert_eq!(gpslat.display_value().with_unit(&reader).to_string(),
|
assert_eq!(gpslat.display_value().with_unit(&exif).to_string(),
|
||||||
"10 deg 0 min 0 sec [GPSLatitudeRef missing]");
|
"10 deg 0 min 0 sec [GPSLatitudeRef missing]");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn heif() {
|
fn heif() {
|
||||||
let file = std::fs::File::open("tests/exif.heic").unwrap();
|
let file = std::fs::File::open("tests/exif.heic").unwrap();
|
||||||
let reader = Reader::read_from_container(
|
let exif = Reader::new().read_from_container(
|
||||||
&mut std::io::BufReader::new(&file)).unwrap();
|
&mut std::io::BufReader::new(&file)).unwrap();
|
||||||
assert_eq!(reader.fields().len(), 2);
|
assert_eq!(exif.fields().len(), 2);
|
||||||
let exifver = reader.get_field(Tag::ExifVersion, In::PRIMARY).unwrap();
|
let exifver = exif.get_field(Tag::ExifVersion, In::PRIMARY).unwrap();
|
||||||
assert_eq!(exifver.display_value().to_string(), "2.31");
|
assert_eq!(exifver.display_value().to_string(), "2.31");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -379,9 +379,9 @@ impl Field {
|
||||||
///
|
///
|
||||||
/// To print the value with the unit, call `with_unit` method on the
|
/// To print the value with the unit, call `with_unit` method on the
|
||||||
/// returned object. It takes a parameter, which is either `()`,
|
/// returned object. It takes a parameter, which is either `()`,
|
||||||
/// `&Field`, or `&Reader` and provides the unit information.
|
/// `&Field`, or `&Exif` and provides the unit information.
|
||||||
/// If the unit does not depend on another field, `()` can be used.
|
/// If the unit does not depend on another field, `()` can be used.
|
||||||
/// Otherwise, `&Field` or `&Reader` should be used.
|
/// Otherwise, `&Field` or `&Exif` should be used.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
|
|
|
@ -37,7 +37,7 @@ use std::path::Path;
|
||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use exif::Error;
|
use exif::Error;
|
||||||
use exif::{In, Reader, Value, Tag};
|
use exif::{Exif, In, Reader, Value, Tag};
|
||||||
use exif::experimental::Writer;
|
use exif::experimental::Writer;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -61,24 +61,25 @@ fn rwr_compare<P>(path: P) where P: AsRef<Path> {
|
||||||
|
|
||||||
// Read.
|
// Read.
|
||||||
let file = File::open(path).unwrap();
|
let file = File::open(path).unwrap();
|
||||||
|
let mut bufreader = BufReader::new(&file);
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
let reader1 = Reader::new(&mut BufReader::new(&file)).unwrap();
|
let exif1 = Reader::new().read_from_container(&mut bufreader).unwrap();
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
let reader1 = match Reader::new(&mut BufReader::new(&file)) {
|
let exif1 = match Reader::new().read_from_container(&mut bufreader) {
|
||||||
Ok(reader) => reader,
|
Ok(exif) => exif,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("{}: {}: Skipped", path.display(), e);
|
println!("{}: {}: Skipped", path.display(), e);
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let strips = get_strips(&reader1, In::PRIMARY);
|
let strips = get_strips(&exif1, In::PRIMARY);
|
||||||
let tn_strips = get_strips(&reader1, In::THUMBNAIL);
|
let tn_strips = get_strips(&exif1, In::THUMBNAIL);
|
||||||
let tiles = get_tiles(&reader1, In::PRIMARY);
|
let tiles = get_tiles(&exif1, In::PRIMARY);
|
||||||
let tn_jpeg = get_jpeg(&reader1, In::THUMBNAIL);
|
let tn_jpeg = get_jpeg(&exif1, In::THUMBNAIL);
|
||||||
|
|
||||||
// Write.
|
// Write.
|
||||||
let mut writer = Writer::new();
|
let mut writer = Writer::new();
|
||||||
for f in reader1.fields() {
|
for f in exif1.fields() {
|
||||||
writer.push_field(f);
|
writer.push_field(f);
|
||||||
}
|
}
|
||||||
if let Some(ref strips) = strips {
|
if let Some(ref strips) = strips {
|
||||||
|
@ -95,9 +96,9 @@ fn rwr_compare<P>(path: P) where P: AsRef<Path> {
|
||||||
}
|
}
|
||||||
let mut out = Cursor::new(Vec::new());
|
let mut out = Cursor::new(Vec::new());
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
writer.write(&mut out, reader1.little_endian()).unwrap();
|
writer.write(&mut out, exif1.little_endian()).unwrap();
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
match writer.write(&mut out, reader1.little_endian()) {
|
match writer.write(&mut out, exif1.little_endian()) {
|
||||||
Ok(_) => {},
|
Ok(_) => {},
|
||||||
Err(Error::NotSupported(_)) => {
|
Err(Error::NotSupported(_)) => {
|
||||||
println!("{}: Contains unknown type", path.display());
|
println!("{}: Contains unknown type", path.display());
|
||||||
|
@ -108,12 +109,12 @@ fn rwr_compare<P>(path: P) where P: AsRef<Path> {
|
||||||
let out = out.into_inner();
|
let out = out.into_inner();
|
||||||
|
|
||||||
// Re-read.
|
// Re-read.
|
||||||
let reader2 = Reader::new(&mut &out[..]).unwrap();
|
let exif2 = Reader::new().read_raw(out).unwrap();
|
||||||
|
|
||||||
// Sort the fields (some files have wrong tag order).
|
// Sort the fields (some files have wrong tag order).
|
||||||
let mut fields1 = reader1.fields().collect::<Vec<_>>();
|
let mut fields1 = exif1.fields().collect::<Vec<_>>();
|
||||||
fields1.sort_by_key(|f| (f.ifd_num, f.tag));
|
fields1.sort_by_key(|f| (f.ifd_num, f.tag));
|
||||||
let mut fields2 = reader2.fields().collect::<Vec<_>>();
|
let mut fields2 = exif2.fields().collect::<Vec<_>>();
|
||||||
fields2.sort_by_key(|f| (f.ifd_num, f.tag));
|
fields2.sort_by_key(|f| (f.ifd_num, f.tag));
|
||||||
|
|
||||||
// Compare.
|
// Compare.
|
||||||
|
@ -128,10 +129,10 @@ fn rwr_compare<P>(path: P) where P: AsRef<Path> {
|
||||||
}
|
}
|
||||||
compare_field_value(&f1.value, &f2.value);
|
compare_field_value(&f1.value, &f2.value);
|
||||||
}
|
}
|
||||||
assert_eq!(get_strips(&reader2, In::PRIMARY), strips);
|
assert_eq!(get_strips(&exif2, In::PRIMARY), strips);
|
||||||
assert_eq!(get_strips(&reader2, In::THUMBNAIL), tn_strips);
|
assert_eq!(get_strips(&exif2, In::THUMBNAIL), tn_strips);
|
||||||
assert_eq!(get_tiles(&reader2, In::PRIMARY), tiles);
|
assert_eq!(get_tiles(&exif2, In::PRIMARY), tiles);
|
||||||
assert_eq!(get_jpeg(&reader2, In::THUMBNAIL), tn_jpeg);
|
assert_eq!(get_jpeg(&exif2, In::THUMBNAIL), tn_jpeg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare field values.
|
// Compare field values.
|
||||||
|
@ -176,27 +177,27 @@ fn compare_field_value(value1: &Value, value2: &Value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_strips(reader: &Reader, ifd_num: In) -> Option<Vec<&[u8]>> {
|
fn get_strips(exif: &Exif, ifd_num: In) -> Option<Vec<&[u8]>> {
|
||||||
let offsets = reader.get_field(Tag::StripOffsets, ifd_num)
|
let offsets = exif.get_field(Tag::StripOffsets, ifd_num)
|
||||||
.and_then(|f| f.value.iter_uint());
|
.and_then(|f| f.value.iter_uint());
|
||||||
let counts = reader.get_field(Tag::StripByteCounts, ifd_num)
|
let counts = exif.get_field(Tag::StripByteCounts, ifd_num)
|
||||||
.and_then(|f| f.value.iter_uint());
|
.and_then(|f| f.value.iter_uint());
|
||||||
let (offsets, counts) = match (offsets, counts) {
|
let (offsets, counts) = match (offsets, counts) {
|
||||||
(Some(offsets), Some(counts)) => (offsets, counts),
|
(Some(offsets), Some(counts)) => (offsets, counts),
|
||||||
(None, None) => return None,
|
(None, None) => return None,
|
||||||
_ => panic!("inconsistent strip offsets and byte counts"),
|
_ => panic!("inconsistent strip offsets and byte counts"),
|
||||||
};
|
};
|
||||||
let buf = reader.buf();
|
let buf = exif.buf();
|
||||||
assert_eq!(offsets.len(), counts.len());
|
assert_eq!(offsets.len(), counts.len());
|
||||||
let strips = offsets.zip(counts).map(
|
let strips = offsets.zip(counts).map(
|
||||||
|(ofs, cnt)| &buf[ofs as usize .. (ofs + cnt) as usize]).collect();
|
|(ofs, cnt)| &buf[ofs as usize .. (ofs + cnt) as usize]).collect();
|
||||||
Some(strips)
|
Some(strips)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_tiles(reader: &Reader, ifd_num: In) -> Option<Vec<&[u8]>> {
|
fn get_tiles(exif: &Exif, ifd_num: In) -> Option<Vec<&[u8]>> {
|
||||||
let offsets = reader.get_field(Tag::TileOffsets, ifd_num)
|
let offsets = exif.get_field(Tag::TileOffsets, ifd_num)
|
||||||
.and_then(|f| f.value.iter_uint());
|
.and_then(|f| f.value.iter_uint());
|
||||||
let counts = reader.get_field(Tag::TileByteCounts, ifd_num)
|
let counts = exif.get_field(Tag::TileByteCounts, ifd_num)
|
||||||
.and_then(|f| f.value.iter_uint());
|
.and_then(|f| f.value.iter_uint());
|
||||||
let (offsets, counts) = match (offsets, counts) {
|
let (offsets, counts) = match (offsets, counts) {
|
||||||
(Some(offsets), Some(counts)) => (offsets, counts),
|
(Some(offsets), Some(counts)) => (offsets, counts),
|
||||||
|
@ -204,22 +205,22 @@ fn get_tiles(reader: &Reader, ifd_num: In) -> Option<Vec<&[u8]>> {
|
||||||
_ => panic!("inconsistent tile offsets and byte counts"),
|
_ => panic!("inconsistent tile offsets and byte counts"),
|
||||||
};
|
};
|
||||||
assert_eq!(offsets.len(), counts.len());
|
assert_eq!(offsets.len(), counts.len());
|
||||||
let buf = reader.buf();
|
let buf = exif.buf();
|
||||||
let strips = offsets.zip(counts).map(
|
let strips = offsets.zip(counts).map(
|
||||||
|(ofs, cnt)| &buf[ofs as usize .. (ofs + cnt) as usize]).collect();
|
|(ofs, cnt)| &buf[ofs as usize .. (ofs + cnt) as usize]).collect();
|
||||||
Some(strips)
|
Some(strips)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_jpeg(reader: &Reader, ifd_num: In) -> Option<&[u8]> {
|
fn get_jpeg(exif: &Exif, ifd_num: In) -> Option<&[u8]> {
|
||||||
let offset = reader.get_field(Tag::JPEGInterchangeFormat, ifd_num)
|
let offset = exif.get_field(Tag::JPEGInterchangeFormat, ifd_num)
|
||||||
.and_then(|f| f.value.get_uint(0));
|
.and_then(|f| f.value.get_uint(0));
|
||||||
let len = reader.get_field(Tag::JPEGInterchangeFormatLength, ifd_num)
|
let len = exif.get_field(Tag::JPEGInterchangeFormatLength, ifd_num)
|
||||||
.and_then(|f| f.value.get_uint(0));
|
.and_then(|f| f.value.get_uint(0));
|
||||||
let (offset, len) = match (offset, len) {
|
let (offset, len) = match (offset, len) {
|
||||||
(Some(offset), Some(len)) => (offset as usize, len as usize),
|
(Some(offset), Some(len)) => (offset as usize, len as usize),
|
||||||
(None, None) => return None,
|
(None, None) => return None,
|
||||||
_ => panic!("inconsistent JPEG offset and length"),
|
_ => panic!("inconsistent JPEG offset and length"),
|
||||||
};
|
};
|
||||||
let buf = reader.buf();
|
let buf = exif.buf();
|
||||||
Some(&buf[offset..offset+len])
|
Some(&buf[offset..offset+len])
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue