From b9575efd0b28555eba6639c6991500ab83058713 Mon Sep 17 00:00:00 2001 From: KAMADA Ken'ichi Date: Sat, 20 Apr 2019 23:16:01 +0900 Subject: [PATCH] Add struct In (IFD number) to indicate primary/thumbnail images. --- examples/dumpexif.rs | 6 +-- examples/reading.rs | 10 ++-- src/lib.rs | 8 ++- src/reader.rs | 44 +++++++++------- src/tiff.rs | 117 +++++++++++++++++++++++++++++++------------ src/writer.rs | 61 +++++++++++----------- tests/exif.tif | Bin 597 -> 631 bytes tests/rwrcmp.rs | 38 +++++++------- 8 files changed, 174 insertions(+), 110 deletions(-) diff --git a/examples/dumpexif.rs b/examples/dumpexif.rs index 3ca6f3b..697f569 100644 --- a/examples/dumpexif.rs +++ b/examples/dumpexif.rs @@ -46,9 +46,9 @@ fn dump_file(path: &Path) -> Result<(), exif::Error> { println!("{}", path.display()); for f in reader.fields() { - let thumb = if f.thumbnail { "1/" } else { "0/" }; - println!(" {}{}: {}", - thumb, f.tag, f.display_value().with_unit(&reader)); + println!(" {}/{}: {}", + f.ifd_num.index(), f.tag, + f.display_value().with_unit(&reader)); if let exif::Value::Ascii(ref s) = f.value { println!(" Ascii({:?})", s.iter().map(escape).collect::>()); diff --git a/examples/reading.rs b/examples/reading.rs index 1df19ab..0bacb1c 100644 --- a/examples/reading.rs +++ b/examples/reading.rs @@ -29,7 +29,7 @@ extern crate exif; use std::fs::File; use std::io::BufReader; -use exif::{DateTime, Reader, Value, Tag}; +use exif::{DateTime, In, Reader, Value, Tag}; fn main() { let file = File::open("tests/exif.jpg").unwrap(); @@ -44,7 +44,7 @@ fn main() { Tag::ImageDescription, Tag::DateTime]; for &tag in tag_list.iter() { - if let Some(field) = reader.get_field(tag, false) { + if let Some(field) = reader.get_field(tag, In::PRIMARY) { println!("{}: {}", field.tag, field.display_value().with_unit(&reader)); } @@ -52,7 +52,7 @@ fn main() { // To get unsigned integer value(s) from either of BYTE, SHORT, // or LONG, `Value::get_uint` or `Value::iter_uint` can be used. - if let Some(field) = reader.get_field(Tag::PixelXDimension, false) { + if let Some(field) = reader.get_field(Tag::PixelXDimension, In::PRIMARY) { if let Some(width) = field.value.get_uint(0) { println!("Valid width of the image is {}.", width); } @@ -60,7 +60,7 @@ fn main() { // To convert a Rational or SRational to an f64, `Rational::to_f64` // or `SRational::to_f64` can be used. - if let Some(field) = reader.get_field(Tag::XResolution, false) { + if let Some(field) = reader.get_field(Tag::XResolution, In::PRIMARY) { match field.value { Value::Rational(ref vec) if !vec.is_empty() => println!("X resolution is {}.", vec[0].to_f64()), @@ -69,7 +69,7 @@ fn main() { } // To parse a DateTime-like field, `DateTime::from_ascii` can be used. - if let Some(field) = reader.get_field(Tag::DateTime, false) { + if let Some(field) = reader.get_field(Tag::DateTime, In::PRIMARY) { match field.value { Value::Ascii(ref vec) if !vec.is_empty() => { if let Ok(datetime) = DateTime::from_ascii(vec[0]) { diff --git a/src/lib.rs b/src/lib.rs index 3ebae76..65fcf13 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,7 +39,7 @@ //! &mut std::io::BufReader::new(&file)).unwrap(); //! for f in reader.fields() { //! println!("{} {} {}", -//! f.tag, f.thumbnail, f.display_value().with_unit(&reader)); +//! f.tag, f.ifd_num, f.display_value().with_unit(&reader)); //! } //! } //! ``` @@ -50,12 +50,16 @@ //! //! * The constants in tag module (`tag::TagName`) have been removed. //! Use `Tag::TagName` instead. +//! * Sturct `In` (IFD number) has been added to indicate primary/thumbnail +//! images, which were distinguished by `bool` previously. Function +//! parameters and struct members now take `In`s instead of `bool`s. +//! `Field::thumbnail` was renamed to `Field::ifd_num` accordingly. pub use error::Error; pub use jpeg::get_exif_attr as get_exif_attr_from_jpeg; pub use reader::Reader; pub use tag::{Context, Tag}; -pub use tiff::{DateTime, Field}; +pub use tiff::{DateTime, Field, In}; pub use tiff::parse_exif; pub use value::Value; pub use value::{Rational, SRational}; diff --git a/src/reader.rs b/src/reader.rs index 37b2009..ae0cdfa 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -33,17 +33,17 @@ use crate::error::Error; use crate::jpeg; use crate::tag::Tag; use crate::tiff; -use crate::tiff::{Field, ProvideUnit}; +use crate::tiff::{Field, In, ProvideUnit}; /// The `Reader` struct reads a JPEG or TIFF image, /// parses the Exif attributes in it, and holds the results. /// /// # Examples /// ``` +/// use exif::{In, Reader, Tag}; /// let file = std::fs::File::open("tests/exif.jpg").unwrap(); -/// let reader = exif::Reader::new( -/// &mut std::io::BufReader::new(&file)).unwrap(); -/// let xres = reader.get_field(exif::Tag::XResolution, false).unwrap(); +/// let reader = Reader::new(&mut std::io::BufReader::new(&file)).unwrap(); +/// let xres = reader.get_field(Tag::XResolution, In::PRIMARY).unwrap(); /// assert_eq!(xres.display_value().with_unit(&reader).to_string(), /// "72 pixels per inch"); /// ``` @@ -68,7 +68,7 @@ pub struct Reader { // True if the TIFF data is little endian. little_endian: bool, // HashMap to find a field quickly. - field_map: HashMap<(Tag, bool), &'static Field<'static>>, + field_map: HashMap<(Tag, In), &'static Field<'static>>, } impl Reader { @@ -98,7 +98,7 @@ impl Reader { // Initialize the HashMap of all fields. let mut field_map = HashMap::new(); for f in &fields { - field_map.insert((f.tag, f.thumbnail), + field_map.insert((f.tag, f.ifd_num), unsafe { mem::transmute::<&Field, &Field>(f) }); } @@ -129,16 +129,16 @@ impl Reader { } /// Returns a reference to the Exif field specified by the tag - /// and the thumbnail flag. + /// and the IFD number. #[inline] - pub fn get_field(&self, tag: Tag, thumbnail: bool) -> Option<&Field> { - self.field_map.get(&(tag, thumbnail)).map(|&f| f) + pub fn get_field(&self, tag: Tag, ifd_num: In) -> Option<&Field> { + self.field_map.get(&(tag, ifd_num)).map(|&f| f) } } impl<'a> ProvideUnit<'a> for &'a Reader { - fn get_field(self, tag: Tag, thumbnail: bool) -> Option<&'a Field<'a>> { - self.get_field(tag, thumbnail) + fn get_field(self, tag: Tag, ifd_num: In) -> Option<&'a Field<'a>> { + self.get_field(tag, ifd_num) } } @@ -199,10 +199,16 @@ mod tests { #[test] fn get_field() { - let file = File::open("tests/exif.jpg").unwrap(); + let file = File::open("tests/exif.tif").unwrap(); let reader = Reader::new(&mut BufReader::new(&file)).unwrap(); - assert_pat!(reader.get_field(Tag::ExifVersion, false).unwrap().value, - Value::Undefined(b"0230", _)); + match reader.get_field(Tag::ImageDescription, In(0)).unwrap().value { + Value::Ascii(ref vec) => assert_eq!(vec, &[b"Test image"]), + ref v => panic!("wrong variant {:?}", v) + } + match reader.get_field(Tag::ImageDescription, In(1)).unwrap().value { + Value::Ascii(ref vec) => assert_eq!(vec, &[b"Test thumbnail"]), + ref v => panic!("wrong variant {:?}", v) + } } #[test] @@ -210,23 +216,23 @@ mod tests { let file = File::open("tests/unit.tif").unwrap(); let reader = Reader::new(&mut BufReader::new(&file)).unwrap(); // No unit. - let exifver = reader.get_field(Tag::ExifVersion, false).unwrap(); + let exifver = reader.get_field(Tag::ExifVersion, In::PRIMARY).unwrap(); assert_eq!(exifver.display_value().with_unit(&reader).to_string(), "2.31"); // Fixed string. - let width = reader.get_field(Tag::ImageWidth, false).unwrap(); + let width = reader.get_field(Tag::ImageWidth, In::PRIMARY).unwrap(); assert_eq!(width.display_value().with_unit(&reader).to_string(), "15 pixels"); // Unit tag (with a non-default value). - let gpsalt = reader.get_field(Tag::GPSAltitude, false).unwrap(); + let gpsalt = reader.get_field(Tag::GPSAltitude, In::PRIMARY).unwrap(); assert_eq!(gpsalt.display_value().with_unit(&reader).to_string(), "0.5 meters below sea level"); // Unit tag is missing but the default is specified. - let xres = reader.get_field(Tag::XResolution, false).unwrap(); + let xres = reader.get_field(Tag::XResolution, In::PRIMARY).unwrap(); assert_eq!(xres.display_value().with_unit(&reader).to_string(), "72 pixels per inch"); // Unit tag is missing and the default is not specified. - let gpslat = reader.get_field(Tag::GPSLatitude, false).unwrap(); + let gpslat = reader.get_field(Tag::GPSLatitude, In::PRIMARY).unwrap(); assert_eq!(gpslat.display_value().with_unit(&reader).to_string(), "10 deg 0 min 0 sec [GPSLatitudeRef missing]"); } diff --git a/src/tiff.rs b/src/tiff.rs index f34aa57..60da204 100644 --- a/src/tiff.rs +++ b/src/tiff.rs @@ -46,12 +46,48 @@ pub const TIFF_LE_SIG: [u8; 4] = [0x49, 0x49, 0x2a, 0x00]; pub struct Field<'a> { /// The tag of this field. pub tag: Tag, - /// False for the primary image and true for the thumbnail. - pub thumbnail: bool, + /// The index of the IFD to which this field belongs. + pub ifd_num: In, /// The value of this field. pub value: Value<'a>, } +/// The IFD number. +/// +/// The IFDs are indexed from 0. The 0th IFD is for the primary image +/// and the 1st one is for the thumbnail. Two associated constants, +/// `In::PRIMARY` and `In::THUMBNAIL`, are defined for them respectively. +/// +/// # Examples +/// ``` +/// use exif::In; +/// assert_eq!(In::PRIMARY.index(), 0); +/// assert_eq!(In::THUMBNAIL.index(), 1); +/// ``` +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct In(pub u16); + +impl In { + pub const PRIMARY: In = In(0); + pub const THUMBNAIL: In = In(1); + + /// Returns the IFD number. + #[inline] + pub fn index(self) -> u16 { + self.0 + } +} + +impl fmt::Display for In { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.0 { + 0 => f.pad("primary"), + 1 => f.pad("thumbnail"), + n => f.pad(&format!("IFD{}", n)), + } + } +} + /// Parse the Exif attributes in the TIFF format. /// /// Returns a Vec of Exif fields and a bool. @@ -77,13 +113,13 @@ fn parse_exif_sub(data: &[u8]) } let ifd_offset = E::loadu32(data, 4) as usize; let mut fields = Vec::new(); - parse_ifd::(&mut fields, data, ifd_offset, Context::Tiff, false)?; + parse_ifd::(&mut fields, data, ifd_offset, Context::Tiff, 0)?; Ok(fields) } // Parse IFD [EXIF23 4.6.2]. fn parse_ifd<'a, E>(fields: &mut Vec>, data: &'a [u8], - offset: usize, ctx: Context, thumbnail: bool) + offset: usize, ctx: Context, ifd_num: u16) -> Result<(), Error> where E: Endian { // Count (the number of the entries). if data.len() < offset || data.len() - offset < 2 { @@ -121,13 +157,13 @@ fn parse_ifd<'a, E>(fields: &mut Vec>, data: &'a [u8], let tag = Tag(ctx, tag); match tag { Tag::ExifIFDPointer => parse_child_ifd::( - fields, data, &val, Context::Exif, thumbnail)?, + fields, data, &val, Context::Exif, ifd_num)?, Tag::GPSInfoIFDPointer => parse_child_ifd::( - fields, data, &val, Context::Gps, thumbnail)?, + fields, data, &val, Context::Gps, ifd_num)?, Tag::InteropIFDPointer => parse_child_ifd::( - fields, data, &val, Context::Interop, thumbnail)?, - _ => fields.push(Field { tag: tag, thumbnail: thumbnail, - value: val }), + fields, data, &val, Context::Interop, ifd_num)?, + _ => fields.push(Field { + tag: tag, ifd_num: In(ifd_num), value: val }), } } @@ -137,24 +173,24 @@ fn parse_ifd<'a, E>(fields: &mut Vec>, data: &'a [u8], } let next_ifd_offset = E::loadu32(data, offset + 2 + count * 12) as usize; // Ignore IFDs after IFD1 (thumbnail) for now. - if next_ifd_offset == 0 || thumbnail { + if next_ifd_offset == 0 || ifd_num > 0 { return Ok(()); } if ctx != Context::Tiff { return Err(Error::InvalidFormat("Unexpected next IFD")); } - parse_ifd::(fields, data, next_ifd_offset, Context::Tiff, true) + parse_ifd::(fields, data, next_ifd_offset, Context::Tiff, 1) } fn parse_child_ifd<'a, E>(fields: &mut Vec>, data: &'a [u8], - pointer: &Value, ctx: Context, thumbnail: bool) + pointer: &Value, ctx: Context, ifd_num: u16) -> Result<(), Error> where E: Endian { // A pointer field has type == LONG and count == 1, so the // value (IFD offset) must be embedded in the "value offset" // element of the field. let ofs = pointer.get_uint(0).ok_or( Error::InvalidFormat("Invalid pointer"))? as usize; - parse_ifd::(fields, data, ofs, ctx, thumbnail) + parse_ifd::(fields, data, ofs, ctx, ifd_num) } pub fn is_tiff(buf: &[u8]) -> bool { @@ -278,16 +314,16 @@ impl<'a> Field<'a> { /// # Examples /// /// ``` - /// use exif::{Field, Tag, Value}; + /// use exif::{Field, In, Tag, Value}; /// /// let xres = Field { /// tag: Tag::XResolution, - /// thumbnail: false, + /// ifd_num: In::PRIMARY, /// value: Value::Rational(vec![(72, 1).into()]), /// }; /// let cm = Field { /// tag: Tag::ResolutionUnit, - /// thumbnail: false, + /// ifd_num: In::PRIMARY, /// value: Value::Short(vec![3]), /// }; /// assert_eq!(xres.display_value().to_string(), "72"); @@ -303,7 +339,7 @@ impl<'a> Field<'a> { /// /// let flen = Field { /// tag: Tag::FocalLengthIn35mmFilm, - /// thumbnail: false, + /// ifd_num: In::PRIMARY, /// value: Value::Short(vec![24]), /// }; /// // The unit of the focal length is always mm, so the argument @@ -315,7 +351,7 @@ impl<'a> Field<'a> { pub fn display_value(&self) -> DisplayValue { DisplayValue { tag: self.tag, - thumbnail: self.thumbnail, + ifd_num: self.ifd_num, value_display: self.value.display_as(self.tag), } } @@ -324,7 +360,7 @@ impl<'a> Field<'a> { /// Helper struct for printing a value in a tag-specific format. pub struct DisplayValue<'a> { tag: Tag, - thumbnail: bool, + ifd_num: In, value_display: value::Display<'a>, } @@ -333,7 +369,7 @@ impl<'a> DisplayValue<'a> { pub fn with_unit<'b, T>(&'b self, unit_provider: T) -> DisplayValueUnit where T: ProvideUnit<'b> { DisplayValueUnit { - thumbnail: self.thumbnail, + ifd_num: self.ifd_num, value_display: self.value_display, unit: self.tag.unit(), unit_provider: unit_provider, @@ -350,7 +386,7 @@ impl<'a> fmt::Display for DisplayValue<'a> { /// Helper struct for printing a value with its unit. pub struct DisplayValueUnit<'a, T> where T: ProvideUnit<'a> { - thumbnail: bool, + ifd_num: In, value_display: value::Display<'a>, unit: Option<&'static [UnitPiece]>, unit_provider: T, @@ -366,7 +402,7 @@ impl<'a, T> fmt::Display for DisplayValueUnit<'a, T> where T: ProvideUnit<'a> { UnitPiece::Str(s) => f.write_str(s), UnitPiece::Tag(tag) => if let Some(x) = self.unit_provider.get_field( - tag, self.thumbnail) { + tag, self.ifd_num) { x.value.display_as(tag).fmt(f) } else if let Some(x) = tag.default_value() { x.display_as(tag).fmt(f) @@ -383,18 +419,18 @@ impl<'a, T> fmt::Display for DisplayValueUnit<'a, T> where T: ProvideUnit<'a> { } pub trait ProvideUnit<'a>: Copy { - fn get_field(self, tag: Tag, thumbnail: bool) -> Option<&'a Field<'a>>; + fn get_field(self, tag: Tag, ifd_num: In) -> Option<&'a Field<'a>>; } impl<'a> ProvideUnit<'a> for () { - fn get_field(self, _tag: Tag, _thumbnail: bool) -> Option<&'a Field<'a>> { + fn get_field(self, _tag: Tag, _ifd_num: In) -> Option<&'a Field<'a>> { None } } impl<'a> ProvideUnit<'a> for &'a Field<'a> { - fn get_field(self, tag: Tag, thumbnail: bool) -> Option<&'a Field<'a>> { - Some(self).filter(|x| x.tag == tag && x.thumbnail == thumbnail) + fn get_field(self, tag: Tag, ifd_num: In) -> Option<&'a Field<'a>> { + Some(self).filter(|x| x.tag == tag && x.ifd_num == ifd_num) } } @@ -402,6 +438,23 @@ impl<'a> ProvideUnit<'a> for &'a Field<'a> { mod tests { use super::*; + #[test] + fn in_convert() { + assert_eq!(In::PRIMARY.index(), 0); + assert_eq!(In::THUMBNAIL.index(), 1); + assert_eq!(In(2).index(), 2); + assert_eq!(In(65535).index(), 65535); + assert_eq!(In::PRIMARY, In(0)); + } + + #[test] + fn in_display() { + assert_eq!(format!("{:10}", In::PRIMARY), "primary "); + assert_eq!(format!("{:>10}", In::THUMBNAIL), " thumbnail"); + assert_eq!(format!("{:10}", In(2)), "IFD2 "); + assert_eq!(format!("{:^10}", In(65535)), " IFD65535 "); + } + // Before the error is returned, the IFD is parsed twice as the // 0th and 1st IFDs. #[test] @@ -464,18 +517,18 @@ mod tests { fn display_value_with_unit() { let cm = Field { tag: Tag::ResolutionUnit, - thumbnail: false, + ifd_num: In::PRIMARY, value: Value::Short(vec![3]), }; let cm_tn = Field { tag: Tag::ResolutionUnit, - thumbnail: true, + ifd_num: In::THUMBNAIL, value: Value::Short(vec![3]), }; // No unit. let exifver = Field { tag: Tag::ExifVersion, - thumbnail: false, + ifd_num: In::PRIMARY, value: Value::Undefined(b"0231", 0), }; assert_eq!(exifver.display_value().to_string(), @@ -487,7 +540,7 @@ mod tests { // Fixed string. let width = Field { tag: Tag::ImageWidth, - thumbnail: false, + ifd_num: In::PRIMARY, value: Value::Short(vec![257]), }; assert_eq!(width.display_value().to_string(), @@ -500,7 +553,7 @@ mod tests { // Unit tag is missing but the default is specified. let xres = Field { tag: Tag::XResolution, - thumbnail: false, + ifd_num: In::PRIMARY, value: Value::Rational(vec![(300, 1).into()]), }; assert_eq!(xres.display_value().to_string(), @@ -514,7 +567,7 @@ mod tests { // Unit tag is missing and the default is not specified. let gpslat = Field { tag: Tag::GPSLatitude, - thumbnail: false, + ifd_num: In::PRIMARY, value: Value::Rational(vec![ (10, 1).into(), (0, 1).into(), (1, 10).into()]), }; diff --git a/src/writer.rs b/src/writer.rs index 6c40544..ba6ead1 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -30,7 +30,7 @@ use std::io::{Seek, SeekFrom, Write}; use crate::endian::{Endian, BigEndian, LittleEndian}; use crate::error::Error; use crate::tag::{Context, Tag}; -use crate::tiff::{Field, TIFF_BE_SIG, TIFF_LE_SIG}; +use crate::tiff::{Field, In, TIFF_BE_SIG, TIFF_LE_SIG}; use crate::value::Value; /// The `Writer` struct is used to encode and write Exif data. @@ -38,11 +38,11 @@ use crate::value::Value; /// # Examples /// /// ``` -/// use exif::{Field, Value, Tag}; +/// use exif::{Field, In, Tag, Value}; /// use exif::experimental::Writer; /// let image_desc = Field { /// tag: Tag::ImageDescription, -/// thumbnail: false, +/// ifd_num: In::PRIMARY, /// value: Value::Ascii(vec![b"Sample"]), /// }; /// let mut writer = Writer::new(); @@ -129,22 +129,23 @@ impl<'a> Writer<'a> { Field { tag: Tag::JPEGInterchangeFormat, .. } | Field { tag: Tag::JPEGInterchangeFormatLength, .. } => {}, // Other normal tags. - Field { tag: Tag(Context::Tiff, _), thumbnail: false, .. } => + Field { tag: Tag(Context::Tiff, _), ifd_num: In::PRIMARY, .. } => self.tiff_fields.push(field), - Field { tag: Tag(Context::Exif, _), thumbnail: false, .. } => + Field { tag: Tag(Context::Exif, _), ifd_num: In::PRIMARY, .. } => self.exif_fields.push(field), - Field { tag: Tag(Context::Gps, _), thumbnail: false, .. } => + Field { tag: Tag(Context::Gps, _), ifd_num: In::PRIMARY, .. } => self.gps_fields.push(field), - Field { tag: Tag(Context::Interop, _), thumbnail: false, .. } => + Field { tag: Tag(Context::Interop, _), ifd_num: In::PRIMARY, .. } => self.interop_fields.push(field), - Field { tag: Tag(Context::Tiff, _), thumbnail: true, .. } => + Field { tag: Tag(Context::Tiff, _), ifd_num: In::THUMBNAIL, .. } => self.tn_tiff_fields.push(field), - Field { tag: Tag(Context::Exif, _), thumbnail: true, .. } => + Field { tag: Tag(Context::Exif, _), ifd_num: In::THUMBNAIL, .. } => self.tn_exif_fields.push(field), - Field { tag: Tag(Context::Gps, _), thumbnail: true, .. } => + Field { tag: Tag(Context::Gps, _), ifd_num: In::THUMBNAIL, .. } => self.tn_gps_fields.push(field), - Field { tag: Tag(Context::Interop, _), thumbnail: true, .. } => + Field { tag: Tag(Context::Interop, _), ifd_num: In::THUMBNAIL, .. } => self.tn_interop_fields.push(field), + _ => unimplemented!(), } } @@ -202,7 +203,7 @@ impl<'a> Writer<'a> { jpeg: None, }; let next_ifd_offset_offset = - synthesize_fields(w, ws, false, little_endian)?; + synthesize_fields(w, ws, In::PRIMARY, little_endian)?; // Do not output the thumbnail IFD if there are no data in it. let thumbnail_absent = @@ -240,7 +241,7 @@ impl<'a> Writer<'a> { tiles: None, jpeg: self.tn_jpeg, }; - synthesize_fields(w, ws, true, little_endian)?; + synthesize_fields(w, ws, In::THUMBNAIL, little_endian)?; w.flush()?; Ok(()) @@ -249,7 +250,7 @@ impl<'a> Writer<'a> { // Synthesizes special fields, writes an image, and returns the offset // of the next IFD offset. -fn synthesize_fields(w: &mut W, ws: WriterState, thumbnail: bool, +fn synthesize_fields(w: &mut W, ws: WriterState, ifd_num: In, little_endian: bool) -> Result where W: Write + Seek { let exif_in_tiff; @@ -267,13 +268,13 @@ fn synthesize_fields(w: &mut W, ws: WriterState, thumbnail: bool, if let Some(strips) = ws.strips { strip_offsets = Field { tag: Tag::StripOffsets, - thumbnail: thumbnail, + ifd_num: ifd_num, value: Value::Long(vec![0; strips.len()]), }; ws.tiff_fields.push(&strip_offsets); strip_byte_counts = Field { tag: Tag::StripByteCounts, - thumbnail: thumbnail, + ifd_num: ifd_num, value: Value::Long( strips.iter().map(|s| s.len() as u32).collect()), }; @@ -282,13 +283,13 @@ fn synthesize_fields(w: &mut W, ws: WriterState, thumbnail: bool, if let Some(tiles) = ws.tiles { tile_offsets = Field { tag: Tag::TileOffsets, - thumbnail: thumbnail, + ifd_num: ifd_num, value: Value::Long(vec![0; tiles.len()]), }; ws.tiff_fields.push(&tile_offsets); tile_byte_counts = Field { tag: Tag::TileByteCounts, - thumbnail: thumbnail, + ifd_num: ifd_num, value: Value::Long( tiles.iter().map(|s| s.len() as u32).collect()), }; @@ -297,13 +298,13 @@ fn synthesize_fields(w: &mut W, ws: WriterState, thumbnail: bool, if let Some(jpeg) = ws.jpeg { jpeg_offset = Field { tag: Tag::JPEGInterchangeFormat, - thumbnail: thumbnail, + ifd_num: ifd_num, value: Value::Long(vec![0]), }; ws.tiff_fields.push(&jpeg_offset); jpeg_length = Field { tag: Tag::JPEGInterchangeFormatLength, - thumbnail: thumbnail, + ifd_num: ifd_num, value: Value::Long(vec![jpeg.len() as u32]), }; ws.tiff_fields.push(&jpeg_length); @@ -322,7 +323,7 @@ fn synthesize_fields(w: &mut W, ws: WriterState, thumbnail: bool, ws.exif_ifd_offset = reserve_ifd(w, exif_fields_len)?; exif_in_tiff = Field { tag: Tag::ExifIFDPointer, - thumbnail: thumbnail, + ifd_num: ifd_num, value: Value::Long(vec![ws.exif_ifd_offset]), }; ws.tiff_fields.push(&exif_in_tiff); @@ -331,7 +332,7 @@ fn synthesize_fields(w: &mut W, ws: WriterState, thumbnail: bool, ws.gps_ifd_offset = reserve_ifd(w, gps_fields_len)?; gps_in_tiff = Field { tag: Tag::GPSInfoIFDPointer, - thumbnail: thumbnail, + ifd_num: ifd_num, value: Value::Long(vec![ws.gps_ifd_offset]), }; ws.tiff_fields.push(&gps_in_tiff); @@ -340,7 +341,7 @@ fn synthesize_fields(w: &mut W, ws: WriterState, thumbnail: bool, ws.interop_ifd_offset = reserve_ifd(w, interop_fields_len)?; interop_in_exif = Field { tag: Tag::InteropIFDPointer, - thumbnail: thumbnail, + ifd_num: ifd_num, value: Value::Long(vec![ws.interop_ifd_offset]), }; ws.exif_fields.push(&interop_in_exif); @@ -615,7 +616,7 @@ mod tests { fn primary() { let image_desc = Field { tag: Tag::ImageDescription, - thumbnail: false, + ifd_num: In::PRIMARY, value: Value::Ascii(vec![b"Sample"]), }; let mut writer = Writer::new(); @@ -634,7 +635,7 @@ mod tests { fn primary_exif_only() { let exif_ver = Field { tag: Tag::ExifVersion, - thumbnail: false, + ifd_num: In::PRIMARY, value: Value::Undefined(b"0231", 0), }; let mut writer = Writer::new(); @@ -707,22 +708,22 @@ mod tests { fn primary_and_thumbnail() { let image_desc = Field { tag: Tag::ImageDescription, - thumbnail: false, + ifd_num: In::PRIMARY, value: Value::Ascii(vec![b"Sample"]), }; let exif_ver = Field { tag: Tag::ExifVersion, - thumbnail: false, + ifd_num: In::PRIMARY, value: Value::Undefined(b"0231", 0), }; let gps_ver = Field { tag: Tag::GPSVersionID, - thumbnail: false, + ifd_num: In::PRIMARY, value: Value::Byte(vec![2, 3, 0, 0]), }; let interop_index = Field { tag: Tag::InteroperabilityIndex, - thumbnail: false, + ifd_num: In::PRIMARY, value: Value::Ascii(vec![b"ABC"]), }; let jpeg = b"JPEG"; @@ -759,7 +760,7 @@ mod tests { fn write_twice() { let image_desc = Field { tag: Tag::ImageDescription, - thumbnail: false, + ifd_num: In::PRIMARY, value: Value::Ascii(vec![b"Sample"]), }; let mut writer = Writer::new(); diff --git a/tests/exif.tif b/tests/exif.tif index c6e17ed25999006782c70e07781553a122f3c078..9e23505d5970c3edacc165fd8de8b8774083adce 100644 GIT binary patch delta 48 zcmcc0@||VE4JM|rjkl^9l^7Tp8TlBP7#JA%85o!{fHXr$YH^7|Nk(aIQeI+a4g&xz C#S6~> delta 13 Ucmey)a+PJm4JHPLjkl^90VqBNMgRZ+ diff --git a/tests/rwrcmp.rs b/tests/rwrcmp.rs index 040dada..f986759 100644 --- a/tests/rwrcmp.rs +++ b/tests/rwrcmp.rs @@ -37,7 +37,7 @@ use std::path::Path; #[cfg(not(test))] use exif::Error; -use exif::{Reader, Value, Tag}; +use exif::{In, Reader, Value, Tag}; use exif::experimental::Writer; #[test] @@ -71,10 +71,10 @@ fn rwr_compare

(path: P) where P: AsRef { return; }, }; - let strips = get_strips(&reader1, false); - let tn_strips = get_strips(&reader1, true); - let tiles = get_tiles(&reader1, false); - let tn_jpeg = get_jpeg(&reader1, true); + let strips = get_strips(&reader1, In::PRIMARY); + let tn_strips = get_strips(&reader1, In::THUMBNAIL); + let tiles = get_tiles(&reader1, In::PRIMARY); + let tn_jpeg = get_jpeg(&reader1, In::THUMBNAIL); // Write. let mut writer = Writer::new(); @@ -120,7 +120,7 @@ fn rwr_compare

(path: P) where P: AsRef { assert_eq!(reader1.fields().len(), reader2.fields().len()); for (f1, f2) in fields1.iter().zip(fields2.iter()) { assert_eq!(f1.tag, f2.tag); - assert_eq!(f1.thumbnail, f2.thumbnail); + assert_eq!(f1.ifd_num, f2.ifd_num); match f1.tag { Tag::StripOffsets | Tag::TileOffsets | Tag::JPEGInterchangeFormat => continue, @@ -128,10 +128,10 @@ fn rwr_compare

(path: P) where P: AsRef { } compare_field_value(&f1.value, &f2.value); } - assert_eq!(get_strips(&reader2, false), strips); - assert_eq!(get_strips(&reader2, true), tn_strips); - assert_eq!(get_tiles(&reader2, false), tiles); - assert_eq!(get_jpeg(&reader2, true), tn_jpeg); + assert_eq!(get_strips(&reader2, In::PRIMARY), strips); + assert_eq!(get_strips(&reader2, In::THUMBNAIL), tn_strips); + assert_eq!(get_tiles(&reader2, In::PRIMARY), tiles); + assert_eq!(get_jpeg(&reader2, In::THUMBNAIL), tn_jpeg); } // Compare field values. @@ -176,10 +176,10 @@ fn compare_field_value(value1: &Value, value2: &Value) { } } -fn get_strips(reader: &Reader, thumbnail: bool) -> Option> { - let offsets = reader.get_field(Tag::StripOffsets, thumbnail) +fn get_strips(reader: &Reader, ifd_num: In) -> Option> { + let offsets = reader.get_field(Tag::StripOffsets, ifd_num) .and_then(|f| f.value.iter_uint()); - let counts = reader.get_field(Tag::StripByteCounts, thumbnail) + let counts = reader.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), @@ -193,10 +193,10 @@ fn get_strips(reader: &Reader, thumbnail: bool) -> Option> { Some(strips) } -fn get_tiles(reader: &Reader, thumbnail: bool) -> Option> { - let offsets = reader.get_field(Tag::TileOffsets, thumbnail) +fn get_tiles(reader: &Reader, ifd_num: In) -> Option> { + let offsets = reader.get_field(Tag::TileOffsets, ifd_num) .and_then(|f| f.value.iter_uint()); - let counts = reader.get_field(Tag::TileByteCounts, thumbnail) + let counts = reader.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), @@ -210,10 +210,10 @@ fn get_tiles(reader: &Reader, thumbnail: bool) -> Option> { Some(strips) } -fn get_jpeg(reader: &Reader, thumbnail: bool) -> Option<&[u8]> { - let offset = reader.get_field(Tag::JPEGInterchangeFormat, thumbnail) +fn get_jpeg(reader: &Reader, ifd_num: In) -> Option<&[u8]> { + let offset = reader.get_field(Tag::JPEGInterchangeFormat, ifd_num) .and_then(|f| f.value.get_uint(0)); - let len = reader.get_field(Tag::JPEGInterchangeFormatLength, thumbnail) + let len = reader.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),