Do not reference the raw buffer from Value and Field.
Previously, they borrowed the buffer, so users had some difficulty to manage the lifetime of the buffer and Value/Field. For example, a user structure was unable to contain both the buffer and a Field.
This commit is contained in:
parent
1533be5a2a
commit
1bd083cbae
|
@ -12,3 +12,6 @@ readme = "README"
|
|||
keywords = ["Exif", "JPEG", "parser", "reader", "TIFF"]
|
||||
categories = ["multimedia::encoding", "parser-implementations"]
|
||||
license = "BSD-2-Clause"
|
||||
|
||||
[dependencies]
|
||||
mutate_once = "0.1.1"
|
||||
|
|
|
@ -49,9 +49,9 @@ fn dump_file(path: &Path) -> Result<(), exif::Error> {
|
|||
println!(" {}/{}: {}",
|
||||
f.ifd_num.index(), f.tag,
|
||||
f.display_value().with_unit(&reader));
|
||||
if let exif::Value::Ascii(ref s) = f.value {
|
||||
if let exif::Value::Ascii(ref v) = f.value {
|
||||
println!(" Ascii({:?})",
|
||||
s.iter().map(escape).collect::<Vec<_>>());
|
||||
v.iter().map(|x| escape(x)).collect::<Vec<_>>());
|
||||
} else {
|
||||
println!(" {:?}", f.value);
|
||||
}
|
||||
|
@ -59,9 +59,9 @@ fn dump_file(path: &Path) -> Result<(), exif::Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn escape(bytes: &&[u8]) -> String {
|
||||
fn escape(bytes: &[u8]) -> String {
|
||||
let mut buf = String::new();
|
||||
for &c in *bytes {
|
||||
for &c in bytes {
|
||||
match c {
|
||||
b'\\' | b'"' => write!(buf, "\\{}", c as char).unwrap(),
|
||||
0x20..=0x7e => buf.write_char(c as char).unwrap(),
|
||||
|
|
|
@ -72,7 +72,7 @@ fn main() {
|
|||
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]) {
|
||||
if let Ok(datetime) = DateTime::from_ascii(&vec[0]) {
|
||||
println!("Year of DateTime is {}.", datetime.year);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -57,13 +57,15 @@
|
|||
//! * The type of `Context` was changed from enum to struct. The variants
|
||||
//! (e.g., `Context::Tiff`) were changed to associated constants and
|
||||
//! they are now spelled in all uppercase (e.g., `Context::TIFF`).
|
||||
//! * `Value` became a self-contained type. The structures of `Value::Ascii`
|
||||
//! and `Value::Undefined` have been changed to use Vec<u8> instead of &[u8].
|
||||
|
||||
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, In};
|
||||
pub use tiff::parse_exif;
|
||||
pub use tiff::parse_exif_compat03 as parse_exif;
|
||||
pub use value::Value;
|
||||
pub use value::{Rational, SRational};
|
||||
|
||||
|
|
|
@ -64,11 +64,11 @@ pub struct Reader {
|
|||
// TIFF data.
|
||||
buf: Vec<u8>,
|
||||
// Exif fields.
|
||||
fields: Vec<Field<'static>>,
|
||||
fields: Vec<Field>,
|
||||
// True if the TIFF data is little endian.
|
||||
little_endian: bool,
|
||||
// HashMap to find a field quickly.
|
||||
field_map: HashMap<(Tag, In), &'static Field<'static>>,
|
||||
field_map: HashMap<(Tag, In), &'static Field>,
|
||||
}
|
||||
|
||||
impl Reader {
|
||||
|
@ -89,11 +89,7 @@ impl Reader {
|
|||
return Err(Error::InvalidFormat("Unknown image format"));
|
||||
}
|
||||
|
||||
// Cheat on the type system and erase the lifetime by transmute().
|
||||
// The scope releases the inner `v` to unborrow `buf`.
|
||||
let (fields, le) = {
|
||||
let (v, le) = tiff::parse_exif(&buf)?;
|
||||
(unsafe { mem::transmute::<Vec<Field>, Vec<Field>>(v) }, le) };
|
||||
let (fields, le) = tiff::parse_exif_compat03(&buf)?;
|
||||
|
||||
// Initialize the HashMap of all fields.
|
||||
let mut field_map = HashMap::new();
|
||||
|
@ -118,7 +114,7 @@ impl Reader {
|
|||
|
||||
/// Returns a slice of Exif fields.
|
||||
#[inline]
|
||||
pub fn fields<'a>(&'a self) -> &[Field<'a>] {
|
||||
pub fn fields<'a>(&'a self) -> &[Field] {
|
||||
&self.fields
|
||||
}
|
||||
|
||||
|
@ -137,7 +133,7 @@ impl Reader {
|
|||
}
|
||||
|
||||
impl<'a> ProvideUnit<'a> for &'a Reader {
|
||||
fn get_field(self, tag: Tag, ifd_num: In) -> Option<&'a Field<'a>> {
|
||||
fn get_field(self, tag: Tag, ifd_num: In) -> Option<&'a Field> {
|
||||
self.get_field(tag, ifd_num)
|
||||
}
|
||||
}
|
||||
|
|
30
src/tag.rs
30
src/tag.rs
|
@ -903,10 +903,10 @@ fn d_sensitivitytype(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
|||
|
||||
// ExifVersion (Exif 0x9000), FlashpixVersion (Exif 0xa000)
|
||||
fn d_exifver(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
||||
if let Value::Undefined(u, _) = *value {
|
||||
if u.len() == 4 {
|
||||
if let Ok(major) = atou16(&u[0..2]) {
|
||||
if let Ok(minor) = atou16(&u[2..4]) {
|
||||
if let Value::Undefined(ref v, _) = *value {
|
||||
if v.len() == 4 {
|
||||
if let Ok(major) = atou16(&v[0..2]) {
|
||||
if let Ok(minor) = atou16(&v[2..4]) {
|
||||
if minor % 10 == 0 {
|
||||
return write!(w, "{}.{}", major, minor / 10);
|
||||
} else {
|
||||
|
@ -921,8 +921,8 @@ fn d_exifver(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
|||
|
||||
// ComponentsConfiguration (Exif 0x9101)
|
||||
fn d_cpntcfg(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
||||
if let Value::Undefined(u, _) = *value {
|
||||
for &x in u {
|
||||
if let Value::Undefined(ref v, _) = *value {
|
||||
for x in v {
|
||||
match x {
|
||||
0 => w.write_char('_'),
|
||||
1 => w.write_char('Y'),
|
||||
|
@ -1092,7 +1092,7 @@ fn d_sensingmethod(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
|||
// FileSource (Exif 0xa300)
|
||||
fn d_filesrc(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
||||
let s = match match *value {
|
||||
Value::Undefined(s, _) => s.first().map(|&x| x),
|
||||
Value::Undefined(ref v, _) => v.first().map(|&x| x),
|
||||
_ => None,
|
||||
} {
|
||||
Some(0) => "others",
|
||||
|
@ -1107,7 +1107,7 @@ fn d_filesrc(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
|||
// SceneType (Exif 0xa301)
|
||||
fn d_scenetype(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
||||
let s = match match *value {
|
||||
Value::Undefined(s, _) => s.first().map(|&x| x),
|
||||
Value::Undefined(ref v, _) => v.first().map(|&x| x),
|
||||
_ => None,
|
||||
} {
|
||||
Some(1) => "directly photographed image",
|
||||
|
@ -1305,7 +1305,7 @@ fn d_gpstimestamp(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
|||
// GPSStatus (Exif/GPS 0x9)
|
||||
fn d_gpsstatus(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
||||
let s = match match *value {
|
||||
Value::Ascii(ref v) => v.first().map(|&x| x),
|
||||
Value::Ascii(ref v) => v.first().map(|x| &x[..]),
|
||||
_ => None,
|
||||
} {
|
||||
Some(b"A") => "measurement in progress",
|
||||
|
@ -1318,7 +1318,7 @@ fn d_gpsstatus(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
|||
// GPSMeasure (Exif/GPS 0xa)
|
||||
fn d_gpsmeasuremode(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
||||
let s = match match *value {
|
||||
Value::Ascii(ref v) => v.first().map(|&x| x),
|
||||
Value::Ascii(ref v) => v.first().map(|x| &x[..]),
|
||||
_ => None,
|
||||
} {
|
||||
Some(b"2") => "2-dimensional measurement",
|
||||
|
@ -1331,7 +1331,7 @@ fn d_gpsmeasuremode(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
|||
// GPSSpeedRef (Exif/GPS 0xc)
|
||||
fn d_gpsspeedref(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
||||
let s = match match *value {
|
||||
Value::Ascii(ref v) => v.first().map(|&x| x),
|
||||
Value::Ascii(ref v) => v.first().map(|x| &x[..]),
|
||||
_ => None,
|
||||
} {
|
||||
Some(b"K") => "km/h",
|
||||
|
@ -1346,7 +1346,7 @@ fn d_gpsspeedref(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
|||
// GPSDestBearingRef (Exif/GPS 0x17)
|
||||
fn d_gpsdirref(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
||||
let s = match match *value {
|
||||
Value::Ascii(ref v) => v.first().map(|&x| x),
|
||||
Value::Ascii(ref v) => v.first().map(|x| &x[..]),
|
||||
_ => None,
|
||||
} {
|
||||
Some(b"T") => "true direction",
|
||||
|
@ -1359,7 +1359,7 @@ fn d_gpsdirref(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
|||
// GPSDestDistanceRef (Exif/GPS 0x19)
|
||||
fn d_gpsdistref(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
||||
let s = match match *value {
|
||||
Value::Ascii(ref v) => v.first().map(|&x| x),
|
||||
Value::Ascii(ref v) => v.first().map(|x| &x[..]),
|
||||
_ => None,
|
||||
} {
|
||||
Some(b"K") => "km",
|
||||
|
@ -1401,7 +1401,7 @@ fn d_gpsdifferential(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
|||
|
||||
fn d_ascii_in_undef(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
||||
match *value {
|
||||
Value::Undefined(s, _) => d_sub_ascii(w, s),
|
||||
Value::Undefined(ref v, _) => d_sub_ascii(w, v),
|
||||
_ => d_default(w, value),
|
||||
}
|
||||
}
|
||||
|
@ -1439,7 +1439,7 @@ fn d_default(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
|
|||
Value::Long(ref v) => d_sub_comma(w, v),
|
||||
Value::Rational(ref v) => d_sub_comma(w, v),
|
||||
Value::SByte(ref v) => d_sub_comma(w, v),
|
||||
Value::Undefined(s, _) => d_sub_hex(w, s),
|
||||
Value::Undefined(ref v, _) => d_sub_hex(w, v),
|
||||
Value::SShort(ref v) => d_sub_comma(w, v),
|
||||
Value::SLong(ref v) => d_sub_comma(w, v),
|
||||
Value::SRational(ref v) => d_sub_comma(w, v),
|
||||
|
|
122
src/tiff.rs
122
src/tiff.rs
|
@ -25,6 +25,7 @@
|
|||
//
|
||||
|
||||
use std::fmt;
|
||||
use mutate_once::MutOnce;
|
||||
|
||||
use crate::endian::{Endian, BigEndian, LittleEndian};
|
||||
use crate::error::Error;
|
||||
|
@ -41,15 +42,56 @@ const TIFF_FORTY_TWO: u16 = 0x002a;
|
|||
pub const TIFF_BE_SIG: [u8; 4] = [0x4d, 0x4d, 0x00, 0x2a];
|
||||
pub const TIFF_LE_SIG: [u8; 4] = [0x49, 0x49, 0x2a, 0x00];
|
||||
|
||||
// Partially parsed TIFF field (IFD entry).
|
||||
// Value::Unknown is abused to represent a partially parsed value.
|
||||
// Such a value must never be exposed to the users of this library.
|
||||
#[derive(Debug)]
|
||||
pub struct IfdEntry {
|
||||
// When partially parsed, the value is stored as Value::Unknown.
|
||||
// Do not leak this field to the outside.
|
||||
field: MutOnce<Field>,
|
||||
}
|
||||
|
||||
impl IfdEntry {
|
||||
fn into_field(self, data: &[u8], le: bool) -> Field {
|
||||
self.parse(data, le);
|
||||
self.field.into_inner()
|
||||
}
|
||||
|
||||
fn parse(&self, data: &[u8], le: bool) {
|
||||
if !self.field.is_fixed() {
|
||||
let mut field = self.field.get_mut();
|
||||
if le {
|
||||
Self::parse_value::<LittleEndian>(&mut field.value, data);
|
||||
} else {
|
||||
Self::parse_value::<BigEndian>(&mut field.value, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a partially parsed value into a real one.
|
||||
fn parse_value<E>(value: &mut Value, data: &[u8]) where E: Endian {
|
||||
match *value {
|
||||
Value::Unknown(typ, cnt, ofs) => {
|
||||
let (unitlen, parser) = get_type_info::<E>(typ);
|
||||
if unitlen != 0 {
|
||||
*value = parser(data, ofs as usize, cnt as usize);
|
||||
}
|
||||
},
|
||||
_ => panic!("value is already parsed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A TIFF field.
|
||||
#[derive(Debug)]
|
||||
pub struct Field<'a> {
|
||||
pub struct Field {
|
||||
/// The tag of this field.
|
||||
pub tag: Tag,
|
||||
/// The index of the IFD to which this field belongs.
|
||||
pub ifd_num: In,
|
||||
/// The value of this field.
|
||||
pub value: Value<'a>,
|
||||
pub value: Value,
|
||||
}
|
||||
|
||||
/// The IFD number.
|
||||
|
@ -93,7 +135,15 @@ impl fmt::Display for In {
|
|||
/// Returns a Vec of Exif fields and a bool.
|
||||
/// The boolean value is true if the data is little endian.
|
||||
/// If an error occurred, `exif::Error` is returned.
|
||||
pub fn parse_exif(data: &[u8]) -> Result<(Vec<Field>, bool), Error> {
|
||||
pub fn parse_exif_compat03(data: &[u8]) -> Result<(Vec<Field>, bool), Error> {
|
||||
parse_exif(data).map(|(entries, le)| {
|
||||
let fields = entries.into_iter()
|
||||
.map(|e| e.into_field(data, le)).collect();
|
||||
(fields, le)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_exif(data: &[u8]) -> Result<(Vec<IfdEntry>, bool), Error> {
|
||||
// Check the byte order and call the real parser.
|
||||
if data.len() < 8 {
|
||||
return Err(Error::InvalidFormat("Truncated TIFF header"));
|
||||
|
@ -106,14 +156,14 @@ pub fn parse_exif(data: &[u8]) -> Result<(Vec<Field>, bool), Error> {
|
|||
}
|
||||
|
||||
fn parse_exif_sub<E>(data: &[u8])
|
||||
-> Result<Vec<Field>, Error> where E: Endian {
|
||||
-> Result<Vec<IfdEntry>, Error> where E: Endian {
|
||||
// Parse the rest of the header (42 and the IFD offset).
|
||||
if E::loadu16(data, 2) != TIFF_FORTY_TWO {
|
||||
return Err(Error::InvalidFormat("Invalid forty two"));
|
||||
}
|
||||
let mut ifd_offset = E::loadu32(data, 4) as usize;
|
||||
let mut ifd_num_ck = Some(0);
|
||||
let mut fields = Vec::new();
|
||||
let mut entries = Vec::new();
|
||||
while ifd_offset != 0 {
|
||||
let ifd_num = ifd_num_ck.ok_or(Error::InvalidFormat("Too many IFDs"))?;
|
||||
// Limit the number of IFDs to defend against resource exhaustion
|
||||
|
@ -122,16 +172,16 @@ fn parse_exif_sub<E>(data: &[u8])
|
|||
return Err(Error::InvalidFormat("Limit the IFD count to 8"));
|
||||
}
|
||||
ifd_offset = parse_ifd::<E>(
|
||||
&mut fields, data, ifd_offset, Context::TIFF, ifd_num)?;
|
||||
&mut entries, data, ifd_offset, Context::TIFF, ifd_num)?;
|
||||
ifd_num_ck = ifd_num.checked_add(1);
|
||||
}
|
||||
Ok(fields)
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
// Parse IFD [EXIF23 4.6.2].
|
||||
fn parse_ifd<'a, E>(fields: &mut Vec<Field<'a>>, data: &'a [u8],
|
||||
offset: usize, ctx: Context, ifd_num: u16)
|
||||
-> Result<usize, Error> where E: Endian {
|
||||
fn parse_ifd<E>(entries: &mut Vec<IfdEntry>, data: &[u8],
|
||||
offset: usize, ctx: Context, ifd_num: u16)
|
||||
-> Result<usize, Error> where E: Endian {
|
||||
// Count (the number of the entries).
|
||||
if data.len() < offset || data.len() - offset < 2 {
|
||||
return Err(Error::InvalidFormat("Truncated IFD count"));
|
||||
|
@ -145,36 +195,33 @@ fn parse_ifd<'a, E>(fields: &mut Vec<Field<'a>>, data: &'a [u8],
|
|||
for i in 0..count as usize {
|
||||
let tag = E::loadu16(data, offset + 2 + i * 12);
|
||||
let typ = E::loadu16(data, offset + 2 + i * 12 + 2);
|
||||
let cnt = E::loadu32(data, offset + 2 + i * 12 + 4) as usize;
|
||||
let cnt = E::loadu32(data, offset + 2 + i * 12 + 4);
|
||||
let valofs_at = offset + 2 + i * 12 + 8;
|
||||
let (unitlen, parser) = get_type_info::<E>(typ);
|
||||
let vallen = unitlen.checked_mul(cnt).ok_or(
|
||||
let (unitlen, _parser) = get_type_info::<E>(typ);
|
||||
let vallen = unitlen.checked_mul(cnt as usize).ok_or(
|
||||
Error::InvalidFormat("Invalid entry count"))?;
|
||||
let val;
|
||||
if unitlen == 0 {
|
||||
val = Value::Unknown(typ, cnt as u32, valofs_at as u32);
|
||||
} else if vallen <= 4 {
|
||||
val = parser(data, valofs_at, cnt);
|
||||
let mut val = if vallen <= 4 {
|
||||
Value::Unknown(typ, cnt, valofs_at as u32)
|
||||
} else {
|
||||
let ofs = E::loadu32(data, valofs_at) as usize;
|
||||
if data.len() < ofs || data.len() - ofs < vallen {
|
||||
return Err(Error::InvalidFormat("Truncated field value"));
|
||||
}
|
||||
val = parser(data, ofs, cnt);
|
||||
}
|
||||
Value::Unknown(typ, cnt, ofs as u32)
|
||||
};
|
||||
|
||||
// No infinite recursion will occur because the context is not
|
||||
// recursively defined.
|
||||
let tag = Tag(ctx, tag);
|
||||
match tag {
|
||||
Tag::ExifIFDPointer => parse_child_ifd::<E>(
|
||||
fields, data, &val, Context::EXIF, ifd_num)?,
|
||||
entries, data, &mut val, Context::EXIF, ifd_num)?,
|
||||
Tag::GPSInfoIFDPointer => parse_child_ifd::<E>(
|
||||
fields, data, &val, Context::GPS, ifd_num)?,
|
||||
entries, data, &mut val, Context::GPS, ifd_num)?,
|
||||
Tag::InteropIFDPointer => parse_child_ifd::<E>(
|
||||
fields, data, &val, Context::INTEROP, ifd_num)?,
|
||||
_ => fields.push(Field {
|
||||
tag: tag, ifd_num: In(ifd_num), value: val }),
|
||||
entries, data, &mut val, Context::INTEROP, ifd_num)?,
|
||||
_ => entries.push(IfdEntry { field: Field {
|
||||
tag: tag, ifd_num: In(ifd_num), value: val }.into()}),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -186,15 +233,18 @@ fn parse_ifd<'a, E>(fields: &mut Vec<Field<'a>>, data: &'a [u8],
|
|||
Ok(next_ifd_offset)
|
||||
}
|
||||
|
||||
fn parse_child_ifd<'a, E>(fields: &mut Vec<Field<'a>>, data: &'a [u8],
|
||||
pointer: &Value, ctx: Context, ifd_num: u16)
|
||||
-> Result<(), Error> where E: Endian {
|
||||
fn parse_child_ifd<E>(entries: &mut Vec<IfdEntry>, data: &[u8],
|
||||
pointer: &mut Value, ctx: Context, ifd_num: u16)
|
||||
-> Result<(), Error> where E: Endian {
|
||||
// The pointer is not yet parsed, so do it here.
|
||||
IfdEntry::parse_value::<E>(pointer, data);
|
||||
|
||||
// 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;
|
||||
match parse_ifd::<E>(fields, data, ofs, ctx, ifd_num)? {
|
||||
match parse_ifd::<E>(entries, data, ofs, ctx, ifd_num)? {
|
||||
0 => Ok(()),
|
||||
_ => Err(Error::InvalidFormat("Unexpected next IFD")),
|
||||
}
|
||||
|
@ -308,7 +358,7 @@ impl fmt::Display for DateTime {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Field<'a> {
|
||||
impl Field {
|
||||
/// Returns an object that implements `std::fmt::Display` for
|
||||
/// printing the value of this field in a tag-specific format.
|
||||
///
|
||||
|
@ -426,17 +476,17 @@ impl<'a, T> fmt::Display for DisplayValueUnit<'a, T> where T: ProvideUnit<'a> {
|
|||
}
|
||||
|
||||
pub trait ProvideUnit<'a>: Copy {
|
||||
fn get_field(self, tag: Tag, ifd_num: In) -> Option<&'a Field<'a>>;
|
||||
fn get_field(self, tag: Tag, ifd_num: In) -> Option<&'a Field>;
|
||||
}
|
||||
|
||||
impl<'a> ProvideUnit<'a> for () {
|
||||
fn get_field(self, _tag: Tag, _ifd_num: In) -> Option<&'a Field<'a>> {
|
||||
fn get_field(self, _tag: Tag, _ifd_num: In) -> Option<&'a Field> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ProvideUnit<'a> for &'a Field<'a> {
|
||||
fn get_field(self, tag: Tag, ifd_num: In) -> Option<&'a Field<'a>> {
|
||||
impl<'a> ProvideUnit<'a> for &'a Field {
|
||||
fn get_field(self, tag: Tag, ifd_num: In) -> Option<&'a Field> {
|
||||
Some(self).filter(|x| x.tag == tag && x.ifd_num == ifd_num)
|
||||
}
|
||||
}
|
||||
|
@ -487,7 +537,7 @@ mod tests {
|
|||
fn unknown_field() {
|
||||
let data = b"MM\0\x2a\0\0\0\x08\
|
||||
\0\x01\x01\0\xff\xff\0\0\0\x01\0\x14\0\0\0\0\0\0";
|
||||
let (v, _) = parse_exif(data).unwrap();
|
||||
let (v, _) = parse_exif_compat03(data).unwrap();
|
||||
assert_eq!(v.len(), 1);
|
||||
assert_pat!(v[0].value, Value::Unknown(0xffff, 1, 0x12));
|
||||
}
|
||||
|
@ -545,7 +595,7 @@ mod tests {
|
|||
let exifver = Field {
|
||||
tag: Tag::ExifVersion,
|
||||
ifd_num: In::PRIMARY,
|
||||
value: Value::Undefined(b"0231", 0),
|
||||
value: Value::Undefined(b"0231".to_vec(), 0),
|
||||
};
|
||||
assert_eq!(exifver.display_value().to_string(),
|
||||
"2.31");
|
||||
|
|
78
src/value.rs
78
src/value.rs
|
@ -30,13 +30,13 @@ use crate::endian::Endian;
|
|||
|
||||
/// Types and values of TIFF fields (for Exif attributes).
|
||||
#[derive(Debug)]
|
||||
pub enum Value<'a> {
|
||||
pub enum Value {
|
||||
/// Vector of 8-bit unsigned integers.
|
||||
Byte(Vec<u8>),
|
||||
/// Vector of slices of 8-bit bytes containing 7-bit ASCII characters.
|
||||
/// The trailing null characters are not included. Note that
|
||||
/// the 8th bits may present if a non-conforming data is given.
|
||||
Ascii(Vec<&'a [u8]>),
|
||||
Ascii(Vec<Vec<u8>>),
|
||||
/// Vector of 16-bit unsigned integers.
|
||||
Short(Vec<u16>),
|
||||
/// Vector of 32-bit unsigned integers.
|
||||
|
@ -52,7 +52,7 @@ pub enum Value<'a> {
|
|||
/// The interpretation of the value does not generally depend on
|
||||
/// the location, but if it does, the offset information helps.
|
||||
/// When encoding Exif, it is ignored.
|
||||
Undefined(&'a [u8], u32),
|
||||
Undefined(Vec<u8>, u32),
|
||||
/// Vector of 16-bit signed integers. Unused in the Exif specification.
|
||||
SShort(Vec<i16>),
|
||||
/// Vector of 32-bit signed integers.
|
||||
|
@ -72,7 +72,7 @@ pub enum Value<'a> {
|
|||
Unknown(u16, u32, u32),
|
||||
}
|
||||
|
||||
impl<'a> Value<'a> {
|
||||
impl Value {
|
||||
/// Returns an object that implements `std::fmt::Display` for
|
||||
/// printing a value in a tag-specific format.
|
||||
/// The tag of the value is specified as the argument.
|
||||
|
@ -83,7 +83,7 @@ impl<'a> Value<'a> {
|
|||
///
|
||||
/// ```
|
||||
/// use exif::{Value, Tag};
|
||||
/// let val = Value::Undefined(b"0231", 0);
|
||||
/// let val = Value::Undefined(b"0231".to_vec(), 0);
|
||||
/// assert_eq!(val.display_as(Tag::ExifVersion).to_string(), "2.31");
|
||||
/// let val = Value::Short(vec![2]);
|
||||
/// assert_eq!(val.display_as(Tag::ResolutionUnit).to_string(), "inch");
|
||||
|
@ -149,7 +149,7 @@ impl<'a> ExactSizeIterator for UIntIter<'a> {}
|
|||
#[derive(Copy, Clone)]
|
||||
pub struct Display<'a> {
|
||||
pub fmt: fn(&mut dyn fmt::Write, &Value) -> fmt::Result,
|
||||
pub value: &'a Value<'a>,
|
||||
pub value: &'a Value,
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for Display<'a> {
|
||||
|
@ -173,16 +173,18 @@ pub enum DefaultValue {
|
|||
Unspecified,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a DefaultValue> for Option<Value<'a>> {
|
||||
impl From<&DefaultValue> for Option<Value> {
|
||||
fn from(defval: &DefaultValue) -> Option<Value> {
|
||||
match *defval {
|
||||
DefaultValue::None => None,
|
||||
DefaultValue::Byte(s) => Some(Value::Byte(s.to_vec())),
|
||||
DefaultValue::Ascii(s) => Some(Value::Ascii(s.to_vec())),
|
||||
DefaultValue::Ascii(s) => Some(Value::Ascii(
|
||||
s.iter().map(|&x| x.to_vec()).collect())),
|
||||
DefaultValue::Short(s) => Some(Value::Short(s.to_vec())),
|
||||
DefaultValue::Rational(s) => Some(Value::Rational(
|
||||
s.iter().map(|&x| x.into()).collect())),
|
||||
DefaultValue::Undefined(s) => Some(Value::Undefined(s, 0)),
|
||||
DefaultValue::Undefined(s) => Some(Value::Undefined(
|
||||
s.to_vec(), 0)),
|
||||
DefaultValue::ContextDependent => None,
|
||||
DefaultValue::Unspecified => None,
|
||||
}
|
||||
|
@ -295,11 +297,10 @@ fn fmt_rational_sub<T>(f: &mut fmt::Formatter, num: u32, denom: T)
|
|||
}
|
||||
}
|
||||
|
||||
type Parser<'a> = fn(&'a [u8], usize, usize) -> Value<'a>;
|
||||
type Parser = fn(&[u8], usize, usize) -> Value;
|
||||
|
||||
// Return the length of a single value and the parser of the type.
|
||||
pub fn get_type_info<'a, E>(typecode: u16)
|
||||
-> (usize, Parser<'a>) where E: Endian {
|
||||
pub fn get_type_info<E>(typecode: u16) -> (usize, Parser) where E: Endian {
|
||||
match typecode {
|
||||
1 => (1, parse_byte),
|
||||
2 => (1, parse_ascii),
|
||||
|
@ -317,25 +318,23 @@ pub fn get_type_info<'a, E>(typecode: u16)
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_byte<'a>(data: &'a [u8], offset: usize, count: usize)
|
||||
-> Value<'a> {
|
||||
fn parse_byte(data: &[u8], offset: usize, count: usize) -> Value {
|
||||
Value::Byte(data[offset .. offset + count].to_vec())
|
||||
}
|
||||
|
||||
fn parse_ascii<'a>(data: &'a [u8], offset: usize, count: usize)
|
||||
-> Value<'a> {
|
||||
fn parse_ascii(data: &[u8], offset: usize, count: usize) -> Value {
|
||||
// Any ASCII field can contain multiple strings [TIFF6 Image File
|
||||
// Directory].
|
||||
let iter = (&data[offset .. offset + count]).split(|&b| b == b'\0');
|
||||
let mut v: Vec<&[u8]> = iter.collect();
|
||||
if v.last().map_or(false, |&s| s.len() == 0) {
|
||||
let mut v: Vec<Vec<u8>> = iter.map(|x| x.to_vec()).collect();
|
||||
if v.last().map_or(false, |x| x.len() == 0) {
|
||||
v.pop();
|
||||
}
|
||||
Value::Ascii(v)
|
||||
}
|
||||
|
||||
fn parse_short<'a, E>(data: &'a [u8], offset: usize, count: usize)
|
||||
-> Value<'a> where E: Endian {
|
||||
fn parse_short<E>(data: &[u8], offset: usize, count: usize)
|
||||
-> Value where E: Endian {
|
||||
let mut val = Vec::with_capacity(count);
|
||||
for i in 0..count {
|
||||
val.push(E::loadu16(data, offset + i * 2));
|
||||
|
@ -343,8 +342,8 @@ fn parse_short<'a, E>(data: &'a [u8], offset: usize, count: usize)
|
|||
Value::Short(val)
|
||||
}
|
||||
|
||||
fn parse_long<'a, E>(data: &'a [u8], offset: usize, count: usize)
|
||||
-> Value<'a> where E: Endian {
|
||||
fn parse_long<E>(data: &[u8], offset: usize, count: usize)
|
||||
-> Value where E: Endian {
|
||||
let mut val = Vec::with_capacity(count);
|
||||
for i in 0..count {
|
||||
val.push(E::loadu32(data, offset + i * 4));
|
||||
|
@ -352,8 +351,8 @@ fn parse_long<'a, E>(data: &'a [u8], offset: usize, count: usize)
|
|||
Value::Long(val)
|
||||
}
|
||||
|
||||
fn parse_rational<'a, E>(data: &'a [u8], offset: usize, count: usize)
|
||||
-> Value<'a> where E: Endian {
|
||||
fn parse_rational<E>(data: &[u8], offset: usize, count: usize)
|
||||
-> Value where E: Endian {
|
||||
let mut val = Vec::with_capacity(count);
|
||||
for i in 0..count {
|
||||
val.push(Rational {
|
||||
|
@ -364,21 +363,19 @@ fn parse_rational<'a, E>(data: &'a [u8], offset: usize, count: usize)
|
|||
Value::Rational(val)
|
||||
}
|
||||
|
||||
fn parse_sbyte<'a>(data: &'a [u8], offset: usize, count: usize)
|
||||
-> Value<'a> {
|
||||
fn parse_sbyte(data: &[u8], offset: usize, count: usize) -> Value {
|
||||
let bytes = data[offset .. offset + count].into_iter()
|
||||
.map(|x| *x as i8)
|
||||
.collect();
|
||||
Value::SByte(bytes)
|
||||
}
|
||||
|
||||
fn parse_undefined<'a>(data: &'a [u8], offset: usize, count: usize)
|
||||
-> Value<'a> {
|
||||
Value::Undefined(&data[offset .. offset + count], offset as u32)
|
||||
fn parse_undefined(data: &[u8], offset: usize, count: usize) -> Value {
|
||||
Value::Undefined(data[offset .. offset + count].to_vec(), offset as u32)
|
||||
}
|
||||
|
||||
fn parse_sshort<'a, E>(data: &'a [u8], offset: usize, count: usize)
|
||||
-> Value<'a> where E: Endian {
|
||||
fn parse_sshort<E>(data: &[u8], offset: usize, count: usize)
|
||||
-> Value where E: Endian {
|
||||
let mut val = Vec::with_capacity(count);
|
||||
for i in 0..count {
|
||||
val.push(E::loadu16(data, offset + i * 2) as i16);
|
||||
|
@ -386,8 +383,8 @@ fn parse_sshort<'a, E>(data: &'a [u8], offset: usize, count: usize)
|
|||
Value::SShort(val)
|
||||
}
|
||||
|
||||
fn parse_slong<'a, E>(data: &'a [u8], offset: usize, count: usize)
|
||||
-> Value<'a> where E: Endian {
|
||||
fn parse_slong<E>(data: &[u8], offset: usize, count: usize)
|
||||
-> Value where E: Endian {
|
||||
let mut val = Vec::with_capacity(count);
|
||||
for i in 0..count {
|
||||
val.push(E::loadu32(data, offset + i * 4) as i32);
|
||||
|
@ -395,8 +392,8 @@ fn parse_slong<'a, E>(data: &'a [u8], offset: usize, count: usize)
|
|||
Value::SLong(val)
|
||||
}
|
||||
|
||||
fn parse_srational<'a, E>(data: &'a [u8], offset: usize, count: usize)
|
||||
-> Value<'a> where E: Endian {
|
||||
fn parse_srational<E>(data: &[u8], offset: usize, count: usize)
|
||||
-> Value where E: Endian {
|
||||
let mut val = Vec::with_capacity(count);
|
||||
for i in 0..count {
|
||||
val.push(SRational {
|
||||
|
@ -408,8 +405,8 @@ fn parse_srational<'a, E>(data: &'a [u8], offset: usize, count: usize)
|
|||
}
|
||||
|
||||
// TIFF and Rust use IEEE 754 format, so no conversion is required.
|
||||
fn parse_float<'a, E>(data: &'a [u8], offset: usize, count: usize)
|
||||
-> Value<'a> where E: Endian {
|
||||
fn parse_float<E>(data: &[u8], offset: usize, count: usize)
|
||||
-> Value where E: Endian {
|
||||
let mut val = Vec::with_capacity(count);
|
||||
for i in 0..count {
|
||||
val.push(f32::from_bits(E::loadu32(data, offset + i * 4)));
|
||||
|
@ -418,8 +415,8 @@ fn parse_float<'a, E>(data: &'a [u8], offset: usize, count: usize)
|
|||
}
|
||||
|
||||
// TIFF and Rust use IEEE 754 format, so no conversion is required.
|
||||
fn parse_double<'a, E>(data: &'a [u8], offset: usize, count: usize)
|
||||
-> Value<'a> where E: Endian {
|
||||
fn parse_double<E>(data: &[u8], offset: usize, count: usize)
|
||||
-> Value where E: Endian {
|
||||
let mut val = Vec::with_capacity(count);
|
||||
for i in 0..count {
|
||||
val.push(f64::from_bits(E::loadu64(data, offset + i * 8)));
|
||||
|
@ -429,8 +426,7 @@ fn parse_double<'a, E>(data: &'a [u8], offset: usize, count: usize)
|
|||
|
||||
// This is a dummy function and will never be called.
|
||||
#[allow(unused_variables)]
|
||||
fn parse_unknown<'a>(data: &'a [u8], offset: usize, count: usize)
|
||||
-> Value<'a> {
|
||||
fn parse_unknown(data: &[u8], offset: usize, count: usize) -> Value {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ use crate::value::Value;
|
|||
/// let image_desc = Field {
|
||||
/// tag: Tag::ImageDescription,
|
||||
/// ifd_num: In::PRIMARY,
|
||||
/// value: Value::Ascii(vec![b"Sample"]),
|
||||
/// value: Value::Ascii(vec![b"Sample".to_vec()]),
|
||||
/// };
|
||||
/// let mut writer = Writer::new();
|
||||
/// let mut buf = std::io::Cursor::new(Vec::new());
|
||||
|
@ -63,10 +63,10 @@ pub struct Writer<'a> {
|
|||
|
||||
#[derive(Debug, Default)]
|
||||
struct Ifd<'a> {
|
||||
tiff_fields: Vec<&'a Field<'a>>,
|
||||
exif_fields: Vec<&'a Field<'a>>,
|
||||
gps_fields: Vec<&'a Field<'a>>,
|
||||
interop_fields: Vec<&'a Field<'a>>,
|
||||
tiff_fields: Vec<&'a Field>,
|
||||
exif_fields: Vec<&'a Field>,
|
||||
gps_fields: Vec<&'a Field>,
|
||||
interop_fields: Vec<&'a Field>,
|
||||
strips: Option<&'a [&'a [u8]]>,
|
||||
tiles: Option<&'a [&'a [u8]]>,
|
||||
jpeg: Option<&'a [u8]>,
|
||||
|
@ -85,10 +85,10 @@ impl<'a> Ifd<'a> {
|
|||
}
|
||||
|
||||
struct WriterState<'a> {
|
||||
tiff_fields: Vec<&'a Field<'a>>,
|
||||
exif_fields: Vec<&'a Field<'a>>,
|
||||
gps_fields: Vec<&'a Field<'a>>,
|
||||
interop_fields: Vec<&'a Field<'a>>,
|
||||
tiff_fields: Vec<&'a Field>,
|
||||
exif_fields: Vec<&'a Field>,
|
||||
gps_fields: Vec<&'a Field>,
|
||||
interop_fields: Vec<&'a Field>,
|
||||
tiff_ifd_offset: u32,
|
||||
exif_ifd_offset: u32,
|
||||
gps_ifd_offset: u32,
|
||||
|
@ -474,8 +474,8 @@ fn compose_value<E>(value: &Value)
|
|||
Ok((1, vec.len(), vec.clone())),
|
||||
Value::Ascii(ref vec) => {
|
||||
let mut buf = Vec::new();
|
||||
for &s in vec {
|
||||
buf.extend_from_slice(s);
|
||||
for x in vec {
|
||||
buf.extend_from_slice(x);
|
||||
buf.push(0);
|
||||
}
|
||||
Ok((2, buf.len(), buf))
|
||||
|
@ -593,7 +593,7 @@ mod tests {
|
|||
let image_desc = Field {
|
||||
tag: Tag::ImageDescription,
|
||||
ifd_num: In::PRIMARY,
|
||||
value: Value::Ascii(vec![b"Sample"]),
|
||||
value: Value::Ascii(vec![b"Sample".to_vec()]),
|
||||
};
|
||||
let mut writer = Writer::new();
|
||||
let mut buf = Cursor::new(Vec::new());
|
||||
|
@ -612,7 +612,7 @@ mod tests {
|
|||
let exif_ver = Field {
|
||||
tag: Tag::ExifVersion,
|
||||
ifd_num: In::PRIMARY,
|
||||
value: Value::Undefined(b"0231", 0),
|
||||
value: Value::Undefined(b"0231".to_vec(), 0),
|
||||
};
|
||||
let mut writer = Writer::new();
|
||||
let mut buf = Cursor::new(Vec::new());
|
||||
|
@ -651,7 +651,7 @@ mod tests {
|
|||
let desc = Field {
|
||||
tag: Tag::ImageDescription,
|
||||
ifd_num: In::PRIMARY,
|
||||
value: Value::Ascii(vec![b"jpg"]),
|
||||
value: Value::Ascii(vec![b"jpg".to_vec()]),
|
||||
};
|
||||
let mut writer = Writer::new();
|
||||
let mut buf = Cursor::new(Vec::new());
|
||||
|
@ -675,7 +675,7 @@ mod tests {
|
|||
let desc = Field {
|
||||
tag: Tag::ImageDescription,
|
||||
ifd_num: In::PRIMARY,
|
||||
value: Value::Ascii(vec![b"tif"]),
|
||||
value: Value::Ascii(vec![b"tif".to_vec()]),
|
||||
};
|
||||
let strips: &[&[u8]] = &[b"STRIP"];
|
||||
let mut writer = Writer::new();
|
||||
|
@ -699,12 +699,12 @@ mod tests {
|
|||
let image_desc = Field {
|
||||
tag: Tag::ImageDescription,
|
||||
ifd_num: In::PRIMARY,
|
||||
value: Value::Ascii(vec![b"Sample"]),
|
||||
value: Value::Ascii(vec![b"Sample".to_vec()]),
|
||||
};
|
||||
let exif_ver = Field {
|
||||
tag: Tag::ExifVersion,
|
||||
ifd_num: In::PRIMARY,
|
||||
value: Value::Undefined(b"0231", 0),
|
||||
value: Value::Undefined(b"0231".to_vec(), 0),
|
||||
};
|
||||
let gps_ver = Field {
|
||||
tag: Tag::GPSVersionID,
|
||||
|
@ -714,7 +714,7 @@ mod tests {
|
|||
let interop_index = Field {
|
||||
tag: Tag::InteroperabilityIndex,
|
||||
ifd_num: In::PRIMARY,
|
||||
value: Value::Ascii(vec![b"ABC"]),
|
||||
value: Value::Ascii(vec![b"ABC".to_vec()]),
|
||||
};
|
||||
let jpeg = b"JPEG";
|
||||
let mut writer = Writer::new();
|
||||
|
@ -751,17 +751,17 @@ mod tests {
|
|||
let desc0 = Field {
|
||||
tag: Tag::ImageDescription,
|
||||
ifd_num: In::PRIMARY,
|
||||
value: Value::Ascii(vec![b"p"]),
|
||||
value: Value::Ascii(vec![b"p".to_vec()]),
|
||||
};
|
||||
let desc1 = Field {
|
||||
tag: Tag::ImageDescription,
|
||||
ifd_num: In::THUMBNAIL,
|
||||
value: Value::Ascii(vec![b"t"]),
|
||||
value: Value::Ascii(vec![b"t".to_vec()]),
|
||||
};
|
||||
let desc2 = Field {
|
||||
tag: Tag::ImageDescription,
|
||||
ifd_num: In(2),
|
||||
value: Value::Ascii(vec![b"2"]),
|
||||
value: Value::Ascii(vec![b"2".to_vec()]),
|
||||
};
|
||||
let mut writer = Writer::new();
|
||||
let mut buf = Cursor::new(Vec::new());
|
||||
|
@ -803,7 +803,7 @@ mod tests {
|
|||
let image_desc = Field {
|
||||
tag: Tag::ImageDescription,
|
||||
ifd_num: In::PRIMARY,
|
||||
value: Value::Ascii(vec![b"Sample"]),
|
||||
value: Value::Ascii(vec![b"Sample".to_vec()]),
|
||||
};
|
||||
let mut writer = Writer::new();
|
||||
writer.push_field(&image_desc);
|
||||
|
@ -820,7 +820,7 @@ mod tests {
|
|||
(Value::Byte(vec![1, 2]),
|
||||
(1, 2, vec![1, 2]),
|
||||
(1, 2, vec![1, 2])),
|
||||
(Value::Ascii(vec![b"a", b"b"]),
|
||||
(Value::Ascii(vec![b"a".to_vec(), b"b".to_vec()]),
|
||||
(2, 4, b"a\0b\0".to_vec()),
|
||||
(2, 4, b"a\0b\0".to_vec())),
|
||||
(Value::Short(vec![0x0102, 0x0304]),
|
||||
|
@ -835,7 +835,7 @@ mod tests {
|
|||
(Value::SByte(vec![-2, -128]),
|
||||
(6, 2, b"\xfe\x80".to_vec()),
|
||||
(6, 2, b"\xfe\x80".to_vec())),
|
||||
(Value::Undefined(b"abc", 0),
|
||||
(Value::Undefined(b"abc".to_vec(), 0),
|
||||
(7, 3, b"abc".to_vec()),
|
||||
(7, 3, b"abc".to_vec())),
|
||||
(Value::SShort(vec![-2, -0x8000]),
|
||||
|
|
Loading…
Reference in New Issue