use crate::byte_order_rw::ByteOrderReader;
use crate::ifd::{Ifd, IfdEntryRef, IfdPath};
use crate::ifd_reader::IfdReader;
use crate::tags::{ifd, IfdType, IfdTypeInterpretation};
use crate::FileType;
use derivative::Derivative;
use std::cell::RefCell;
use std::error::Error;
use std::fmt::{Display, Formatter};
use std::io;
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug)]
pub enum DngReaderError {
IoError(io::Error),
FormatError(String),
Other(String),
}
impl From<io::Error> for DngReaderError {
fn from(e: io::Error) -> Self {
Self::IoError(e)
}
}
impl Display for DngReaderError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
DngReaderError::IoError(e) => f.write_fmt(format_args!("IoError: '{:?}'", e)),
DngReaderError::FormatError(e) => f.write_fmt(format_args!("FormatError: '{}'", e)),
DngReaderError::Other(e) => f.write_fmt(format_args!("Other: '{}'", e)),
}
}
}
impl Error for DngReaderError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
DngReaderError::IoError(e) => Some(e),
DngReaderError::FormatError(_) => None,
DngReaderError::Other(_) => None,
}
}
}
#[derive(Derivative)]
#[derivative(Debug)]
pub struct DngReader<R: Read + Seek> {
file_type: FileType,
#[derivative(Debug = "ignore")]
reader: RefCell<ByteOrderReader<R>>,
ifds: Vec<Ifd>,
}
impl<R: Read + Seek> DngReader<R> {
pub fn read(mut reader: R) -> Result<Self, DngReaderError> {
let mut header = vec![0u8; 2];
reader.read_exact(&mut header)?;
let is_little_endian = match (header[0], header[1]) {
(0x49, 0x49) => Ok(true),
(0x4D, 0x4D) => Ok(false),
(_, _) => Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid header bytes",
)),
}?;
let mut reader = ByteOrderReader::new(reader, is_little_endian);
let magic = reader.read_u16()?;
let file_type = FileType::from_magic(magic).ok_or_else(|| {
DngReaderError::FormatError(format!(
"invalid magic byte sequence (expected 42, got {}",
magic
))
})?;
let mut next_ifd_offset = reader.read_u32()?;
let mut unprocessed_ifds = Vec::new();
while next_ifd_offset != 0 {
reader.seek(SeekFrom::Start(next_ifd_offset as u64))?;
unprocessed_ifds.push(IfdReader::read(&mut reader)?);
next_ifd_offset = reader.read_u32()?;
}
let ifds: Result<Vec<_>, _> = unprocessed_ifds
.iter()
.map(|ifd| ifd.process(IfdType::Ifd, &mut reader))
.collect();
Ok(Self {
reader: RefCell::new(reader),
ifds: ifds?,
file_type,
})
}
pub fn get_ifd0(&self) -> &Ifd {
&self.ifds[0]
}
pub fn get_entry_by_path<'a>(&'a self, path: &'a IfdPath) -> Option<IfdEntryRef<'a>> {
for ifd in &self.ifds {
let result = ifd.get_entry_by_path(path);
if result.is_some() {
return result;
}
}
None
}
pub fn needed_buffer_size_for_offsets(
&self,
entry: IfdEntryRef,
) -> Result<usize, DngReaderError> {
if let Some(IfdTypeInterpretation::Offsets { lengths }) =
entry.tag.get_type_interpretation()
{
let lengths_paths = entry.path.with_last_tag_replaced(lengths.as_maybe());
let lengths_value = self.get_entry_by_path(&lengths_paths);
if let Some(entry) = lengths_value {
entry
.value
.as_u32()
.map(|v| v as usize)
.ok_or(DngReaderError::Other(format!(
"length tag {lengths_paths:?} for {:?} does not have integer value",
entry.path
)))
} else {
Err(DngReaderError::Other(format!(
"length tag {lengths_paths:?} for {:?} not found",
entry.path
)))
}
} else {
Err(DngReaderError::Other(format!(
"entry {entry:?} is not of type offsets"
)))
}
}
pub fn read_offsets_to_buffer(
&self,
entry: IfdEntryRef,
buffer: &mut [u8],
) -> Result<(), DngReaderError> {
let buffer_size = self.needed_buffer_size_for_offsets(entry)?;
if buffer_size != buffer.len() {
Err(DngReaderError::Other(format!(
"buffer has wrong size (expected {buffer_size} found {}",
buffer.len()
)))
} else {
let mut reader = self.reader.borrow_mut();
reader.seek(SeekFrom::Start(entry.value.as_u32().ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("entry {entry:?} cant be read into buffer. it is not a single OFFSETS"),
)
})? as u64))?;
reader.read_exact(buffer)?;
Ok(())
}
}
pub fn main_image_data_ifd_path(&self) -> IfdPath {
self.get_ifd0()
.find_entry(|entry| {
entry.tag == &ifd::NewSubfileType.as_maybe() && entry.value.as_u32() == Some(0)
})
.map(|entry| entry.parent())
.unwrap_or_default()
}
pub fn needed_buffer_length_for_image_data(
&self,
ifd_path: &IfdPath,
) -> Result<usize, DngReaderError> {
if let (Some(offsets), Some(lengths)) = (
self.get_entry_by_path(&ifd_path.chain_tag(ifd::StripOffsets)),
self.get_entry_by_path(&ifd_path.chain_tag(ifd::StripByteCounts)),
) {
lengths
.value
.as_list()
.try_fold(0, |acc, x| {
x.as_u32()
.map(|v| acc + v)
.ok_or(DngReaderError::Other(format!(
"length tag {:?} for {:?} does not have integer value",
lengths.path, offsets.path
)))
})
.map(|v| v as usize)
} else if let (Some(_offsets), Some(_lengths)) = (
self.get_entry_by_path(&ifd_path.chain_tag(ifd::TileOffsets)),
self.get_entry_by_path(&ifd_path.chain_tag(ifd::TileByteCounts)),
) {
Err(DngReaderError::Other(
"reading tiled images is not implemented".to_string(),
))
} else {
Err(DngReaderError::Other(
"No image data was found in the specified IFD or the path didnt lead to an IFD"
.to_string(),
))
}
}
pub fn read_image_data_to_buffer(
&self,
ifd_path: &IfdPath,
buffer: &mut [u8],
) -> Result<(), DngReaderError> {
if let (Some(offsets), Some(lengths)) = (
self.get_entry_by_path(&ifd_path.chain_tag(ifd::StripOffsets)),
self.get_entry_by_path(&ifd_path.chain_tag(ifd::StripByteCounts)),
) {
let mut reader = self.reader.borrow_mut();
let count = offsets.value.get_count();
if count != lengths.value.get_count() {
return Err(DngReaderError::FormatError(
"the counts of OFFSETS and LENGTHS must be the same".to_string(),
));
}
let mut buffer_offset = 0;
for (offset, length) in offsets.value.as_list().zip(lengths.value.as_list()) {
let offset = offset.as_u32().ok_or(DngReaderError::Other(format!(
"offset tag {offset:?} for {:?} does not have integer value",
offsets.path
)))?;
let length = length.as_u32().ok_or(DngReaderError::Other(format!(
"length tag {length:?} for {:?} does not have integer value",
offsets.path
)))?;
reader.seek(SeekFrom::Start(offset as u64))?;
let buffer_slice =
&mut buffer[(buffer_offset as usize)..((buffer_offset + length) as usize)];
reader.read_exact(buffer_slice)?;
buffer_offset += length;
}
Ok(())
} else if let (Some(_offsets), Some(_lengths)) = (
self.get_entry_by_path(&ifd_path.chain_tag(ifd::TileOffsets)),
self.get_entry_by_path(&ifd_path.chain_tag(ifd::TileByteCounts)),
) {
Err(DngReaderError::Other(
"reading tiled images is not implemented".to_string(),
))
} else {
Err(DngReaderError::Other(
"No image data was found in the specified IFD or the path didnt lead to an IFD"
.to_string(),
))
}
}
}