use crate::error::Result;
use crate::metadata::exif::ByteOrderMark;
#[derive(Debug, Clone, Copy)]
pub enum ExifFormat {
Byte = 1,
Ascii = 2,
Short = 3,
Long = 4,
Rational = 5,
SByte = 6,
Undefined = 7,
SShort = 8,
SLong = 9,
SRational = 10,
Float = 11,
Double = 12,
}
impl ExifFormat {
pub fn size(self) -> usize {
match self {
ExifFormat::Byte | ExifFormat::Ascii | ExifFormat::SByte | ExifFormat::Undefined => 1,
ExifFormat::Short | ExifFormat::SShort => 2,
ExifFormat::Long | ExifFormat::SLong | ExifFormat::Float => 4,
ExifFormat::Rational | ExifFormat::SRational | ExifFormat::Double => 8,
}
}
}
#[derive(Debug, Clone)]
pub struct IfdEntry {
pub tag: u16,
pub format: ExifFormat,
pub data: Vec<u8>,
}
pub fn build_exif(
ifd0_entries: &[IfdEntry],
exif_ifd_entries: &[IfdEntry],
gps_entries: &[IfdEntry],
byte_order: ByteOrderMark,
) -> Result<Vec<u8>> {
let mut output = Vec::new();
match byte_order {
ByteOrderMark::LittleEndian => {
output.extend_from_slice(b"II");
output.extend_from_slice(&42u16.to_le_bytes());
output.extend_from_slice(&8u32.to_le_bytes()); }
ByteOrderMark::BigEndian => {
output.extend_from_slice(b"MM");
output.extend_from_slice(&42u16.to_be_bytes());
output.extend_from_slice(&8u32.to_be_bytes());
}
}
let mut all_ifd0 = ifd0_entries.to_vec();
let exif_ifd_pointer_idx = if !exif_ifd_entries.is_empty() {
let idx = all_ifd0.len();
all_ifd0.push(IfdEntry {
tag: 0x8769, format: ExifFormat::Long,
data: vec![0, 0, 0, 0], });
Some(idx)
} else {
None
};
let gps_pointer_idx = if !gps_entries.is_empty() {
let idx = all_ifd0.len();
all_ifd0.push(IfdEntry {
tag: 0x8825, format: ExifFormat::Long,
data: vec![0, 0, 0, 0], });
Some(idx)
} else {
None
};
all_ifd0.sort_by_key(|e| e.tag);
let (ifd0_bytes, ifd0_overflow) = build_ifd(&all_ifd0, byte_order, output.len());
let _ifd0_end = output.len() + ifd0_bytes.len() + ifd0_overflow.len() + 4;
output.extend_from_slice(&ifd0_bytes);
output.extend_from_slice(&write_u32(0, byte_order)); output.extend_from_slice(&ifd0_overflow);
if !exif_ifd_entries.is_empty() {
let exif_ifd_offset = output.len() as u32;
if let Some(idx) = exif_ifd_pointer_idx {
fixup_ifd_pointer(&mut output, &all_ifd0, idx, exif_ifd_offset, byte_order, 8);
}
let mut sorted_exif = exif_ifd_entries.to_vec();
sorted_exif.sort_by_key(|e| e.tag);
let (exif_bytes, exif_overflow) = build_ifd(&sorted_exif, byte_order, output.len());
output.extend_from_slice(&exif_bytes);
output.extend_from_slice(&write_u32(0, byte_order)); output.extend_from_slice(&exif_overflow);
}
if !gps_entries.is_empty() {
let gps_ifd_offset = output.len() as u32;
if let Some(idx) = gps_pointer_idx {
fixup_ifd_pointer(&mut output, &all_ifd0, idx, gps_ifd_offset, byte_order, 8);
}
let mut sorted_gps = gps_entries.to_vec();
sorted_gps.sort_by_key(|e| e.tag);
let (gps_bytes, gps_overflow) = build_ifd(&sorted_gps, byte_order, output.len());
output.extend_from_slice(&gps_bytes);
output.extend_from_slice(&write_u32(0, byte_order));
output.extend_from_slice(&gps_overflow);
}
Ok(output)
}
fn build_ifd(
entries: &[IfdEntry],
byte_order: ByteOrderMark,
base_offset: usize,
) -> (Vec<u8>, Vec<u8>) {
let mut ifd = Vec::new();
let mut overflow = Vec::new();
ifd.extend_from_slice(&write_u16(entries.len() as u16, byte_order));
let overflow_start = base_offset + 2 + entries.len() * 12 + 4;
for entry in entries {
let count = entry.data.len() / entry.format.size().max(1);
ifd.extend_from_slice(&write_u16(entry.tag, byte_order));
ifd.extend_from_slice(&write_u16(entry.format as u16, byte_order));
ifd.extend_from_slice(&write_u32(count as u32, byte_order));
if entry.data.len() <= 4 {
let mut padded = [0u8; 4];
padded[..entry.data.len()].copy_from_slice(&entry.data);
ifd.extend_from_slice(&padded);
} else {
let offset = (overflow_start + overflow.len()) as u32;
ifd.extend_from_slice(&write_u32(offset, byte_order));
overflow.extend_from_slice(&entry.data);
if entry.data.len() % 2 != 0 {
overflow.push(0);
}
}
}
(ifd, overflow)
}
fn fixup_ifd_pointer(
output: &mut [u8],
_entries: &[IfdEntry],
entry_idx: usize,
target_offset: u32,
byte_order: ByteOrderMark,
ifd_start: usize,
) {
let entry_pos = ifd_start + 2 + entry_idx * 12 + 8; if entry_pos + 4 <= output.len() {
let bytes = write_u32(target_offset, byte_order);
output[entry_pos..entry_pos + 4].copy_from_slice(&bytes);
}
}
pub fn encode_ascii(s: &str) -> Vec<u8> {
let mut data = s.as_bytes().to_vec();
data.push(0); data
}
pub fn encode_u16(v: u16, bo: ByteOrderMark) -> Vec<u8> {
write_u16(v, bo).to_vec()
}
pub fn encode_u32(v: u32, bo: ByteOrderMark) -> Vec<u8> {
write_u32(v, bo).to_vec()
}
pub fn encode_urational(num: u32, den: u32, bo: ByteOrderMark) -> Vec<u8> {
let mut data = Vec::with_capacity(8);
data.extend_from_slice(&write_u32(num, bo));
data.extend_from_slice(&write_u32(den, bo));
data
}
pub fn encode_srational(num: i32, den: i32, bo: ByteOrderMark) -> Vec<u8> {
let mut data = Vec::with_capacity(8);
data.extend_from_slice(&write_u32(num as u32, bo));
data.extend_from_slice(&write_u32(den as u32, bo));
data
}
fn write_u16(v: u16, bo: ByteOrderMark) -> [u8; 2] {
match bo {
ByteOrderMark::LittleEndian => v.to_le_bytes(),
ByteOrderMark::BigEndian => v.to_be_bytes(),
}
}
fn write_u32(v: u32, bo: ByteOrderMark) -> [u8; 4] {
match bo {
ByteOrderMark::LittleEndian => v.to_le_bytes(),
ByteOrderMark::BigEndian => v.to_be_bytes(),
}
}