Parse IFD structures and record the context in Tag.
In the Exif specification, IFDs may be nested and chained. The interpretation of a tag value depends on the IFD in which it appears, so the context is added into Tag.
This commit is contained in:
parent
eaaad2418f
commit
6651b7d159
|
@ -28,7 +28,7 @@
|
|||
|
||||
pub use error::Error;
|
||||
pub use jpeg::get_exif_attr as get_exif_attr_from_jpeg;
|
||||
pub use tag::Tag;
|
||||
pub use tag::{Context, Tag};
|
||||
pub use tiff::Field;
|
||||
pub use tiff::parse_exif;
|
||||
pub use value::Value;
|
||||
|
@ -41,7 +41,7 @@ mod tmacro;
|
|||
mod endian;
|
||||
mod error;
|
||||
mod jpeg;
|
||||
mod tag;
|
||||
pub mod tag;
|
||||
mod tiff;
|
||||
mod util;
|
||||
mod value;
|
||||
|
|
90
src/tag.rs
90
src/tag.rs
|
@ -24,6 +24,15 @@
|
|||
// SUCH DAMAGE.
|
||||
//
|
||||
|
||||
//! Compatibility warning: Exif tag constants in this module will be
|
||||
//! converted to associated constants of Tag when the feature is
|
||||
//! stabilized.
|
||||
|
||||
/// A tag of a TIFF field.
|
||||
///
|
||||
/// Use `exif::Tag` instead of `exif::tag::Tag`. They are the same,
|
||||
/// but `exif::tag` will become private in the future versions.
|
||||
//
|
||||
// This is not an enum to keep safety and API stability, while
|
||||
// supporting unknown tag values. This comment is based on the
|
||||
// behavior of Rust 1.12.
|
||||
|
@ -34,7 +43,82 @@
|
|||
// tends to break backward compatibility. When Tag::VariantFoo is
|
||||
// defined in a new version of the library, the old codes using
|
||||
// Tag::Unknown(Foo's value) will break.
|
||||
//
|
||||
// Use of constants is restricted in patterns. As of Rust 1.12,
|
||||
// PartialEq and Eq need to be _automatically derived_ for Tag to
|
||||
// emulate structural equivalency.
|
||||
// <https://github.com/rust-lang/rfcs/pull/1445>
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Tag(pub Context, pub u16);
|
||||
|
||||
/// A tag of a TIFF field.
|
||||
#[derive(Debug)]
|
||||
pub struct Tag(pub u16);
|
||||
impl Tag {
|
||||
/// Returns the context of the tag.
|
||||
#[inline]
|
||||
pub fn context(&self) -> Context {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Returns the value of the tag.
|
||||
#[inline]
|
||||
pub fn value(&self) -> u16 {
|
||||
self.1
|
||||
}
|
||||
}
|
||||
|
||||
/// An enum that indicates how a tag value is interpreted.
|
||||
///
|
||||
/// Use `exif::Context` instead of `exif::tag::Context`. They are the
|
||||
/// same, but `exif::tag` will become private in the future versions.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Context {
|
||||
/// TIFF attributes defined in the TIFF Rev. 6.0 specification.
|
||||
Tiff, // 0th IFD
|
||||
/// Exif attributes.
|
||||
Exif, // 0th IFD -- Exif IFD
|
||||
/// GPS attributes.
|
||||
Gps, // 0th IFD -- GPS IFD
|
||||
/// Interoperability attributes.
|
||||
Interop, // 0th IFD -- Exif IFD -- Interoperability IFD
|
||||
/// TIFF fields in the 1st IFD, which represents the thumbnail image.
|
||||
Thumb, // 1st IFD
|
||||
}
|
||||
|
||||
macro_rules! generate_well_known_tag_constants {
|
||||
(
|
||||
$(
|
||||
// Copy the doc attribute to the actual definition.
|
||||
$( #[$attr:meta] )*
|
||||
($name:ident, $ctx:ident, $num:expr, $desc:expr)
|
||||
),+,
|
||||
) => (
|
||||
$(
|
||||
$( #[$attr] )*
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const $name: Tag = Tag(Context::$ctx, $num);
|
||||
)+
|
||||
)
|
||||
}
|
||||
|
||||
// Tag constant names do not follow the Rust naming conventions but
|
||||
// the Exif field names: camel cases and all-capital acronyms.
|
||||
generate_well_known_tag_constants!(
|
||||
// Exif-specific IFDs [EXIF23 4.6.3].
|
||||
|
||||
/// A pointer to the Exif IFD. This is used for the internal structure
|
||||
/// of Exif data and will not be returned to the user.
|
||||
(ExifIFDPointer, Tiff, 0x8769, "Exif IFD pointer"),
|
||||
/// A pointer to the GPS IFD. This is used for the internal structure
|
||||
/// of Exif data and will not be returned to the user.
|
||||
(GPSInfoIFDPointer, Tiff, 0x8825, "GPS Info IFD pointer"),
|
||||
/// A pointer to the interoperability IFD. This is used for the internal
|
||||
/// structure of Exif data and will not be returned to the user.
|
||||
(InteropIFDPointer, Exif, 0xa005, "Interoperability IFD pointer"),
|
||||
|
||||
// TIFF attributes [EXIF23 4.6.4].
|
||||
|
||||
// Exif IFD attributes [EXIF23 4.6.5].
|
||||
|
||||
// GPS attributes [EXIF23 4.6.6].
|
||||
|
||||
// Interoperability attributes [EXIF23 4.6.7].
|
||||
);
|
||||
|
|
53
src/tiff.rs
53
src/tiff.rs
|
@ -26,7 +26,8 @@
|
|||
|
||||
use endian::{Endian, BigEndian, LittleEndian};
|
||||
use error::Error;
|
||||
use tag::Tag;
|
||||
use tag;
|
||||
use tag::{Context, Tag};
|
||||
use value::Value;
|
||||
use value::get_type_info;
|
||||
|
||||
|
@ -62,11 +63,11 @@ fn parse_exif_sub<E>(data: &[u8])
|
|||
return Err(Error::InvalidFormat("Invalid forty two"));
|
||||
}
|
||||
let ifd_offset = E::loadu32(data, 4) as usize;
|
||||
parse_ifd::<E>(data, ifd_offset)
|
||||
parse_ifd::<E>(data, ifd_offset, Context::Tiff)
|
||||
}
|
||||
|
||||
// Parse IFD [EXIF23 4.6.2].
|
||||
fn parse_ifd<E>(data: &[u8], offset: usize)
|
||||
fn parse_ifd<E>(data: &[u8], offset: usize, ctx: Context)
|
||||
-> Result<Vec<Field>, Error> where E: Endian {
|
||||
// Count (the number of the entries).
|
||||
if data.len() < offset || data.len() - offset < 2 {
|
||||
|
@ -98,17 +99,57 @@ fn parse_ifd<E>(data: &[u8], offset: usize)
|
|||
}
|
||||
val = parser(data, ofs, cnt);
|
||||
}
|
||||
fields.push(Field { tag: Tag(tag), value: val });
|
||||
|
||||
// No infinite recursion will occur because the context is not
|
||||
// recursively defined.
|
||||
// XXX Should we check the type and count of a pointer?
|
||||
// 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 tag = Tag(ctx, tag);
|
||||
if tag == tag::ExifIFDPointer {
|
||||
let mut v = try!(parse_ifd::<E>(data, ofs, Context::Exif));
|
||||
fields.append(&mut v);
|
||||
} else if tag == tag::GPSInfoIFDPointer {
|
||||
let mut v = try!(parse_ifd::<E>(data, ofs, Context::Gps));
|
||||
fields.append(&mut v);
|
||||
} else if tag == tag::InteropIFDPointer {
|
||||
let mut v = try!(parse_ifd::<E>(data, ofs, Context::Interop));
|
||||
fields.append(&mut v);
|
||||
} else {
|
||||
fields.push(Field { tag: tag, value: val });
|
||||
}
|
||||
}
|
||||
|
||||
// Offset to the next IFD.
|
||||
if data.len() - offset - 2 - count * 12 < 4 {
|
||||
return Err(Error::InvalidFormat("Truncated IFD"));
|
||||
}
|
||||
let next_ifd_offset = E::loadu32(data, offset + 2 + count * 12);
|
||||
let next_ifd_offset = E::loadu32(data, offset + 2 + count * 12) as usize;
|
||||
if next_ifd_offset != 0 {
|
||||
unimplemented!();
|
||||
if ctx != Context::Tiff {
|
||||
return Err(Error::InvalidFormat("Unexpected next IFD"));
|
||||
}
|
||||
let mut v = try!(
|
||||
parse_ifd::<E>(data, next_ifd_offset, Context::Thumb));
|
||||
fields.append(&mut v);
|
||||
}
|
||||
|
||||
Ok(fields)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use error::Error;
|
||||
use super::*;
|
||||
|
||||
// Before the error is returned, the IFD is parsed twice as the
|
||||
// 0th and 1st IFDs.
|
||||
#[test]
|
||||
fn inf_loop_by_next() {
|
||||
let data = b"MM\0\x2a\0\0\0\x08\
|
||||
\0\x01\x01\0\0\x03\0\0\0\x01\0\x14\0\0\0\0\0\x08";
|
||||
assert_err_pat!(parse_exif(data),
|
||||
Error::InvalidFormat("Unexpected next IFD"));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue