Add struct In (IFD number) to indicate primary/thumbnail images.

This commit is contained in:
KAMADA Ken'ichi 2019-04-20 23:16:01 +09:00
parent 6c3ddddc96
commit b9575efd0b
8 changed files with 174 additions and 110 deletions

View File

@ -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::<Vec<_>>());

View File

@ -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]) {

View File

@ -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};

View File

@ -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]");
}

View File

@ -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<E>(data: &[u8])
}
let ifd_offset = E::loadu32(data, 4) as usize;
let mut fields = Vec::new();
parse_ifd::<E>(&mut fields, data, ifd_offset, Context::Tiff, false)?;
parse_ifd::<E>(&mut fields, data, ifd_offset, Context::Tiff, 0)?;
Ok(fields)
}
// Parse IFD [EXIF23 4.6.2].
fn parse_ifd<'a, E>(fields: &mut Vec<Field<'a>>, 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<Field<'a>>, data: &'a [u8],
let tag = Tag(ctx, tag);
match tag {
Tag::ExifIFDPointer => parse_child_ifd::<E>(
fields, data, &val, Context::Exif, thumbnail)?,
fields, data, &val, Context::Exif, ifd_num)?,
Tag::GPSInfoIFDPointer => parse_child_ifd::<E>(
fields, data, &val, Context::Gps, thumbnail)?,
fields, data, &val, Context::Gps, ifd_num)?,
Tag::InteropIFDPointer => parse_child_ifd::<E>(
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<Field<'a>>, 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::<E>(fields, data, next_ifd_offset, Context::Tiff, true)
parse_ifd::<E>(fields, data, next_ifd_offset, Context::Tiff, 1)
}
fn parse_child_ifd<'a, E>(fields: &mut Vec<Field<'a>>, 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::<E>(fields, data, ofs, ctx, thumbnail)
parse_ifd::<E>(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<T> 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()]),
};

View File

@ -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>(w: &mut W, ws: WriterState, thumbnail: bool,
fn synthesize_fields<W>(w: &mut W, ws: WriterState, ifd_num: In,
little_endian: bool)
-> Result<u32, Error> where W: Write + Seek {
let exif_in_tiff;
@ -267,13 +268,13 @@ fn synthesize_fields<W>(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>(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>(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>(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>(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>(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();

Binary file not shown.

View File

@ -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<P>(path: P) where P: AsRef<Path> {
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<P>(path: P) where P: AsRef<Path> {
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<P>(path: P) where P: AsRef<Path> {
}
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<Vec<&[u8]>> {
let offsets = reader.get_field(Tag::StripOffsets, thumbnail)
fn get_strips(reader: &Reader, ifd_num: In) -> Option<Vec<&[u8]>> {
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<Vec<&[u8]>> {
Some(strips)
}
fn get_tiles(reader: &Reader, thumbnail: bool) -> Option<Vec<&[u8]>> {
let offsets = reader.get_field(Tag::TileOffsets, thumbnail)
fn get_tiles(reader: &Reader, ifd_num: In) -> Option<Vec<&[u8]>> {
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<Vec<&[u8]>> {
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),