use super::exif::*;
use super::exifpost::*;
use super::ifdformat::*;
use super::lowlevel::*;
use super::types::*;
use std::borrow::Cow;
type InExifResult = Result<(), ExifError>;
pub(crate) fn parse_exif_entry(ifd: IfdEntry, warnings: &mut Vec<String>, kind: IfdKind) -> ExifEntry {
let (tag, unit, format, min_count, max_count, more_readable) = tag_to_exif(ifd.tag);
let value = match tag_value_new(&ifd) {
Some(v) => v,
None => TagValue::Invalid(ifd.data.clone(), ifd.le, ifd.format as u16, ifd.count),
};
let e = ExifEntry {
value_more_readable: more_readable(ifd.tag, &value).unwrap_or(Cow::Borrowed("")),
namespace: ifd.namespace,
ifd,
tag,
unit: unit.into(),
value,
kind,
};
if tag == ExifTag::UnknownToMe {
return e;
}
if (((tag as u32) & 0xffff) as u16) != e.ifd.tag
|| (min_count == -1
&& (format != IfdFormat::Ascii
&& format != IfdFormat::Undefined
&& format != IfdFormat::Unknown))
|| (min_count != -1 && format == IfdFormat::Ascii)
{
panic!("Internal error {:x}", e.ifd.tag);
}
if format != e.ifd.format {
warnings.push(format!(
"EXIF tag {:x} {} ({}), expected format {} ({:?}), found {} ({:?})",
e.ifd.tag, e.ifd.tag, tag, format as u8, format, e.ifd.format as u8, e.ifd.format
));
}
if min_count != -1 && ((e.ifd.count as i32) < min_count || (e.ifd.count as i32) > max_count) {
warnings.push(format!(
"EXIF tag {:x} {} ({:?}), format {}, expected count {}..{} found {}",
e.ifd.tag, e.ifd.tag, tag, format as u8, min_count, max_count, e.ifd.count
));
}
e
}
pub fn parse_ifd(
subifd: bool,
le: bool,
count: u16,
contents: &[u8]
) -> Option<(Vec<IfdEntry>, usize)> {
let mut entries: Vec<IfdEntry> = Vec::new();
for i in 0..count {
let mut offset = (i as usize) * 12;
let tag = read_u16(le, contents.get(offset..)?)?;
offset += 2;
let format = read_u16(le, contents.get(offset..)?)?;
offset += 2;
let count = read_u32(le, contents.get(offset..)?)?;
offset += 4;
let data = contents.get(offset..offset + 4)?.to_vec();
let entry = IfdEntry {
namespace: Namespace::Standard,
tag,
format: IfdFormat::new(format),
count,
ifd_data: data,
le,
ext_data: Vec::new(),
data: Vec::new(),
};
entries.push(entry);
}
let next_ifd = if subifd {
0
} else {
read_u32(le, &contents[count as usize * 12..])? as usize
};
Some((entries, next_ifd))
}
fn parse_exif_ifd(
le: bool,
contents: &[u8],
ioffset: usize,
exif_entries: &mut Vec<ExifEntry>,
warnings: &mut Vec<String>,
kind: IfdKind,
) -> InExifResult {
let mut offset = ioffset;
if contents.len() < (offset + 2) {
return Err(ExifError::ExifIfdTruncated(
format!("Truncated {:?} at dir entry count ({} < {})", kind, contents.len(), (offset + 2)),
));
}
let count = read_u16(
le,
contents
.get(offset..)
.ok_or(ExifError::IfdTruncated)?,
).ok_or(ExifError::IfdTruncated)?;
let ifd_length = (count as usize) * 12;
offset += 2;
if contents.len() < (offset + ifd_length) {
return Err(ExifError::ExifIfdTruncated("Truncated at dir listing".into()));
}
let ifd_content = &contents
.get(offset..offset + ifd_length)
.ok_or(ExifError::IfdTruncated)?;
let (ifd, _) = parse_ifd(true, le, count, ifd_content).ok_or(ExifError::IfdTruncated)?;
for mut entry in ifd {
if !entry.copy_data(contents) {
continue;
}
let exif_entry = parse_exif_entry(entry, warnings, kind);
exif_entries.push(exif_entry);
}
Ok(())
}
pub fn parse_ifds(
le: bool,
ifd0_offset: usize,
contents: &[u8],
warnings: &mut Vec<String>,
) -> ExifEntryResult {
let mut offset = ifd0_offset;
let mut exif_entries: Vec<ExifEntry> = Vec::new();
match parse_exif_ifd(le, contents, offset, &mut exif_entries, warnings, IfdKind::Ifd0) {
Ok(()) => true,
Err(e) => return Err(e),
};
let count = read_u16(
le,
contents
.get(offset..offset + 2)
.ok_or(ExifError::IfdTruncated)?,
).ok_or(ExifError::IfdTruncated)?;
let ifd_length = (count as usize) * 12 + 4;
offset += 2;
let ifd_content = &contents
.get(offset..offset + ifd_length)
.ok_or(ExifError::IfdTruncated)?;
let (ifd, _) = parse_ifd(false, le, count, ifd_content).ok_or(ExifError::IfdTruncated)?;
for entry in &ifd {
let ifd_kind = if entry.tag == (((ExifTag::ExifOffset as u32) & 0xffff) as u16) {
IfdKind::Exif
} else if entry.tag == (((ExifTag::GPSOffset as u32) & 0xffff) as u16) {
IfdKind::Gps
} else {
continue;
};
let exif_offset = entry.try_data_as_offset().unwrap_or(!0);
if contents.len() < exif_offset {
return Err(ExifError::ExifIfdTruncated("Exif SubIFD goes past EOF".into()));
}
parse_exif_ifd(le, contents, exif_offset, &mut exif_entries, warnings, ifd_kind)?;
}
for n in 0..exif_entries.len() {
let (begin, end) = exif_entries.split_at_mut(n);
let (entry, end) = end.split_first_mut().unwrap();
exif_postprocessing(entry, begin, end);
}
Ok(exif_entries)
}
pub fn parse_tiff(contents: &[u8], warnings: &mut Vec<String>) -> (ExifEntryResult, bool) {
let mut le = false;
if contents.len() < 8 {
return (Err(ExifError::TiffTruncated), false);
} else if contents[0] == b'I' && contents[1] == b'I' && contents[2] == 42 && contents[3] == 0 {
le = true;
} else if contents[0] == b'M' && contents[1] == b'M' && contents[2] == 0 && contents[3] == 42 {
} else {
let err = format!("Preamble is {:x} {:x} {:x} {:x}", contents[0], contents[1], contents[2], contents[3]);
return (Err(ExifError::TiffBadPreamble(err)), false);
}
let offset = read_u32(le, &contents[4..]).unwrap() as usize;
(parse_ifds(le, offset, contents, warnings), le)
}