use crate::error::{Error, Result};
const UUID_XMP: [u8; 16] = [
0xBE, 0x7A, 0xCF, 0xCB, 0x97, 0xA9, 0x42, 0xE8, 0x9C, 0x71, 0x99, 0x94, 0x91, 0xE3, 0xAF, 0xAC,
];
pub fn write_jp2(
source: &[u8],
new_xmp: Option<&[u8]>,
new_exif: Option<&[u8]>,
) -> Result<Vec<u8>> {
if source.len() < 12 {
return Err(Error::InvalidData("file too small".into()));
}
let mut output = Vec::with_capacity(source.len());
let mut pos = 0;
let mut wrote_xmp = false;
if source.starts_with(&[0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20]) {
output.extend_from_slice(&source[..12]);
pos = 12;
}
while pos + 8 <= source.len() {
let box_size = u32::from_be_bytes([
source[pos],
source[pos + 1],
source[pos + 2],
source[pos + 3],
]) as usize;
let box_type = &source[pos + 4..pos + 8];
let actual_size = if box_size == 0 {
source.len() - pos
} else {
box_size
};
if actual_size < 8 || pos + actual_size > source.len() {
break;
}
match box_type {
b"uuid"
if actual_size > 24
&& source[pos + 8..pos + 24] == UUID_XMP
&& new_xmp.is_some() =>
{
if let Some(xmp) = new_xmp {
write_box(&mut output, b"uuid", &UUID_XMP, xmp);
wrote_xmp = true;
}
}
b"xml " if new_xmp.is_some() => {
if let Some(xmp) = new_xmp {
let size = (xmp.len() + 8) as u32;
output.extend_from_slice(&size.to_be_bytes());
output.extend_from_slice(b"xml ");
output.extend_from_slice(xmp);
wrote_xmp = true;
}
}
b"Exif" if new_exif.is_some() => {
if let Some(exif) = new_exif {
let size = (exif.len() + 12) as u32; output.extend_from_slice(&size.to_be_bytes());
output.extend_from_slice(b"Exif");
output.extend_from_slice(&[0, 0, 0, 0]); output.extend_from_slice(exif);
}
}
_ => {
output.extend_from_slice(&source[pos..pos + actual_size]);
}
}
pos += actual_size;
}
if !wrote_xmp {
if let Some(xmp) = new_xmp {
write_box(&mut output, b"uuid", &UUID_XMP, xmp);
}
}
Ok(output)
}
fn write_box(output: &mut Vec<u8>, box_type: &[u8; 4], prefix: &[u8], data: &[u8]) {
let size = (8 + prefix.len() + data.len()) as u32;
output.extend_from_slice(&size.to_be_bytes());
output.extend_from_slice(box_type);
output.extend_from_slice(prefix);
output.extend_from_slice(data);
}