tiff_io.rs (65792B)
1 // Copyright 2023 Adobe. All rights reserved. 2 // This file is licensed to you under the Apache License, 3 // Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) 4 // or the MIT license (http://opensource.org/licenses/MIT), 5 // at your option. 6 7 // Unless required by applicable law or agreed to in writing, 8 // this software is distributed on an "AS IS" BASIS, WITHOUT 9 // WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or 10 // implied. See the LICENSE-MIT and LICENSE-APACHE files for the 11 // specific language governing permissions and limitations under 12 // each license. 13 14 use std::{ 15 collections::{BTreeMap, HashMap}, 16 fs::OpenOptions, 17 io::{Cursor, Read, Seek, SeekFrom, Write}, 18 path::Path, 19 vec, 20 }; 21 22 use atree::{Arena, Token}; 23 use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; 24 use byteordered::{with_order, ByteOrdered, Endianness}; 25 use conv::ValueFrom; 26 use tempfile::Builder; 27 28 use crate::{ 29 asset_io::{ 30 rename_or_copy, AssetIO, AssetPatch, CAIRead, CAIReadWrite, CAIReader, CAIWriter, 31 ComposedManifestRef, HashBlockObjectType, HashObjectPositions, RemoteRefEmbed, 32 RemoteRefEmbedType, 33 }, 34 error::{Error, Result}, 35 utils::xmp_inmemory_utils::{add_provenance, MIN_XMP}, 36 }; 37 38 const II: [u8; 2] = *b"II"; 39 const MM: [u8; 2] = *b"MM"; 40 41 const C2PA_TAG: u16 = 0xcd41; 42 const XMP_TAG: u16 = 0x02bc; 43 const SUBFILE_TAG: u16 = 0x014a; 44 const EXIFIFD_TAG: u16 = 0x8769; 45 const GPSIFD_TAG: u16 = 0x8825; 46 const C2PA_FIELD_TYPE: u16 = 7; 47 48 const STRIPBYTECOUNTS: u16 = 279; 49 const STRIPOFFSETS: u16 = 273; 50 const TILEBYTECOUNTS: u16 = 325; 51 const TILEOFFSETS: u16 = 324; 52 53 const SUBFILES: [u16; 3] = [SUBFILE_TAG, EXIFIFD_TAG, GPSIFD_TAG]; 54 55 static SUPPORTED_TYPES: [&str; 10] = [ 56 "tif", 57 "tiff", 58 "image/tiff", 59 "dng", 60 "image/dng", 61 "image/x-adobe-dng", 62 "arw", 63 "image/x-sony-arw", 64 "nef", 65 "image/x-nikon-nef", 66 ]; 67 68 // The type of an IFD entry 69 #[derive(Debug, PartialEq)] 70 enum IFDEntryType { 71 Byte = 1, // 8-bit unsigned integer 72 Ascii = 2, // 8-bit byte that contains a 7-bit ASCII code; the last byte must be zero 73 Short = 3, // 16-bit unsigned integer 74 Long = 4, // 32-bit unsigned integer 75 Rational = 5, // Fraction stored as two 32-bit unsigned integers 76 Sbyte = 6, // 8-bit signed integer 77 Undefined = 7, // 8-bit byte that may contain anything, depending on the field 78 Sshort = 8, // 16-bit signed integer 79 Slong = 9, // 32-bit signed integer 80 Srational = 10, // Fraction stored as two 32-bit signed integers 81 Float = 11, // 32-bit IEEE floating point 82 Double = 12, // 64-bit IEEE floating point 83 Ifd = 13, // 32-bit unsigned integer (offset) 84 Long8 = 16, // BigTIFF 64-bit unsigned integer 85 Slong8 = 17, // BigTIFF 64-bit unsigned integer (offset) 86 Ifd8 = 18, // 64-bit unsigned integer (offset) 87 } 88 89 impl IFDEntryType { 90 pub const fn from_u16(val: u16) -> Option<IFDEntryType> { 91 match val { 92 1 => Some(IFDEntryType::Byte), 93 2 => Some(IFDEntryType::Ascii), 94 3 => Some(IFDEntryType::Short), 95 4 => Some(IFDEntryType::Long), 96 5 => Some(IFDEntryType::Rational), 97 6 => Some(IFDEntryType::Sbyte), 98 7 => Some(IFDEntryType::Undefined), 99 8 => Some(IFDEntryType::Sshort), 100 9 => Some(IFDEntryType::Slong), 101 10 => Some(IFDEntryType::Srational), 102 11 => Some(IFDEntryType::Float), 103 12 => Some(IFDEntryType::Double), 104 13 => Some(IFDEntryType::Ifd), 105 16 => Some(IFDEntryType::Long8), 106 17 => Some(IFDEntryType::Slong8), 107 18 => Some(IFDEntryType::Ifd8), 108 _ => None, 109 } 110 } 111 } 112 113 // TIFF IFD Entry (value_offset is in target endian) 114 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 115 pub struct IfdEntry { 116 entry_tag: u16, 117 entry_type: u16, 118 value_count: u64, 119 value_offset: u64, 120 } 121 122 // helper enum to know if the IFD requires special handling 123 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 124 pub enum IfdType { 125 Page, 126 Subfile, 127 Exif, 128 Gps, 129 } 130 131 // TIFF IFD 132 #[derive(Clone, Debug, PartialEq, Eq)] 133 pub struct ImageFileDirectory { 134 offset: u64, 135 entry_cnt: u64, 136 ifd_type: IfdType, 137 entries: HashMap<u16, IfdEntry>, 138 next_ifd_offset: Option<u64>, 139 } 140 141 impl ImageFileDirectory { 142 #[allow(dead_code)] 143 pub fn get_tag(&self, tag_id: u16) -> Option<&IfdEntry> { 144 self.entries.get(&tag_id) 145 } 146 147 #[allow(dead_code)] 148 pub fn get_tag_mut(&mut self, tag_id: u16) -> Option<&mut IfdEntry> { 149 self.entries.get_mut(&tag_id) 150 } 151 } 152 153 // Struct to map the contents of a TIFF file 154 #[allow(dead_code)] 155 pub(crate) struct TiffStructure { 156 byte_order: Endianness, 157 big_tiff: bool, 158 first_ifd_offset: u64, 159 first_ifd: Option<ImageFileDirectory>, 160 } 161 162 impl TiffStructure { 163 #[allow(dead_code)] 164 pub fn load<R>(reader: &mut R) -> Result<Self> 165 where 166 R: Read + Seek + ?Sized, 167 { 168 let mut endianness = [0u8, 2]; 169 reader.read_exact(&mut endianness)?; 170 171 let byte_order = match endianness { 172 II => Endianness::Little, 173 MM => Endianness::Big, 174 _ => { 175 return Err(Error::InvalidAsset( 176 "Could not parse input image".to_owned(), 177 )) 178 } 179 }; 180 181 let mut byte_reader = ByteOrdered::runtime(reader, byte_order); 182 183 let big_tiff = match byte_reader.read_u16() { 184 Ok(42) => false, 185 Ok(43) => { 186 // read Big TIFF structs 187 // Read byte size of offsets, must be 8 188 if byte_reader.read_u16()? != 8 { 189 return Err(Error::InvalidAsset( 190 "Could not parse input image".to_owned(), 191 )); 192 } 193 // must currently be 0 194 if byte_reader.read_u16()? != 0 { 195 return Err(Error::InvalidAsset( 196 "Could not parse input image".to_owned(), 197 )); 198 } 199 true 200 } 201 _ => { 202 return Err(Error::InvalidAsset( 203 "Could not parse input image".to_owned(), 204 )) 205 } 206 }; 207 208 let first_ifd_offset = if big_tiff { 209 byte_reader.read_u64()? 210 } else { 211 byte_reader.read_u32()?.into() 212 }; 213 214 // move read pointer to IFD 215 byte_reader.seek(SeekFrom::Start(first_ifd_offset))?; 216 let first_ifd = TiffStructure::read_ifd( 217 byte_reader.into_inner(), 218 byte_order, 219 big_tiff, 220 IfdType::Page, 221 )?; 222 223 let ts = TiffStructure { 224 byte_order, 225 big_tiff, 226 first_ifd_offset, 227 first_ifd: Some(first_ifd), 228 }; 229 230 Ok(ts) 231 } 232 233 // read IFD entries, all value_offset are in source endianness 234 pub fn read_ifd_entries<R>( 235 byte_reader: &mut ByteOrdered<&mut R, Endianness>, 236 big_tiff: bool, 237 entry_cnt: u64, 238 entries: &mut HashMap<u16, IfdEntry>, 239 ) -> Result<()> 240 where 241 R: Read + Seek + ?Sized, 242 { 243 for _ in 0..entry_cnt { 244 let tag = byte_reader.read_u16()?; 245 let tag_type = byte_reader.read_u16()?; 246 247 let (count, data_offset) = if big_tiff { 248 let count = byte_reader.read_u64()?; 249 let mut buf = [0; 8]; 250 byte_reader.read_exact(&mut buf)?; 251 252 let data_offset = buf 253 .as_slice() 254 .read_u64::<NativeEndian>() 255 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; 256 (count, data_offset) 257 } else { 258 let count = byte_reader.read_u32()?; 259 let mut buf = [0; 4]; 260 byte_reader.read_exact(&mut buf)?; 261 262 let data_offset = buf 263 .as_slice() 264 .read_u32::<NativeEndian>() 265 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; 266 (count.into(), data_offset.into()) 267 }; 268 269 let ifd_entry = IfdEntry { 270 entry_tag: tag, 271 entry_type: tag_type, 272 value_count: count, 273 value_offset: data_offset, 274 }; 275 276 /* 277 println!( 278 "{}, {}, {}. {:?}", 279 ifd_entry.entry_tag, 280 ifd_entry.entry_type, 281 ifd_entry.value_count, 282 ifd_entry.value_offset.to_ne_bytes() 283 ); 284 */ 285 286 entries.insert(tag, ifd_entry); 287 } 288 289 Ok(()) 290 } 291 292 // read IFD from reader 293 pub fn read_ifd<R>( 294 reader: &mut R, 295 byte_order: Endianness, 296 big_tiff: bool, 297 ifd_type: IfdType, 298 ) -> Result<ImageFileDirectory> 299 where 300 R: Read + Seek + ReadBytesExt + ?Sized, 301 { 302 let mut byte_reader = ByteOrdered::runtime(reader, byte_order); 303 304 let ifd_offset = byte_reader.stream_position()?; 305 //println!("IFD Offset: {:#x}", ifd_offset); 306 307 let entry_cnt = if big_tiff { 308 byte_reader.read_u64()? 309 } else { 310 byte_reader.read_u16()?.into() 311 }; 312 313 let mut ifd = ImageFileDirectory { 314 offset: ifd_offset, 315 entry_cnt, 316 ifd_type, 317 entries: HashMap::new(), 318 next_ifd_offset: None, 319 }; 320 321 TiffStructure::read_ifd_entries(&mut byte_reader, big_tiff, entry_cnt, &mut ifd.entries)?; 322 323 let next_ifd = if big_tiff { 324 byte_reader.read_u64()? 325 } else { 326 byte_reader.read_u32()?.into() 327 }; 328 329 match next_ifd { 330 0 => (), 331 _ => ifd.next_ifd_offset = Some(next_ifd), 332 }; 333 334 Ok(ifd) 335 } 336 } 337 338 // offset are stored in source endianness so to use offset value in Seek calls we must convert to native endianness 339 fn decode_offset(offset_file_native: u64, endianness: Endianness, big_tiff: bool) -> Result<u64> { 340 let offset: u64; 341 let offset_bytes = offset_file_native.to_ne_bytes(); 342 let offset_reader = Cursor::new(offset_bytes); 343 344 with_order!(offset_reader, endianness, |src| { 345 if big_tiff { 346 let o = src.read_u64()?; 347 offset = o; 348 } else { 349 let o = src.read_u32()?; 350 offset = o.into(); 351 } 352 }); 353 354 Ok(offset) 355 } 356 357 fn stream_len(reader: &mut dyn CAIRead) -> crate::Result<u64> { 358 let old_pos = reader.stream_position()?; 359 let len = reader.seek(SeekFrom::End(0))?; 360 361 if old_pos != len { 362 reader.seek(SeekFrom::Start(old_pos))?; 363 } 364 365 Ok(len) 366 } 367 // create tree of TIFF structure IFDs and IFD entries. 368 fn map_tiff<R>(input: &mut R) -> Result<(Arena<ImageFileDirectory>, Token, Endianness, bool)> 369 where 370 R: Read + Seek + ?Sized, 371 { 372 let _size = input.seek(SeekFrom::End(0))?; 373 input.rewind()?; 374 375 let ts = TiffStructure::load(input)?; 376 377 let (tiff_tree, page_0): (Arena<ImageFileDirectory>, Token) = if let Some(ifd) = 378 ts.first_ifd.clone() 379 { 380 let (mut tiff_tree, page_0_token) = Arena::with_data(ifd); 381 /* No multi-page at the moment 382 // get the pages 383 loop { 384 if let Some(next_ifd_offset) = &tiff_tree[current_token].data.next_ifd_offset { 385 input.seek(SeekFrom::Start(*next_ifd_offset))?; 386 387 let next_ifd = TiffStructure::read_ifd(input, ts.byte_order, ts.big_tiff, IFDType::PageIFD)?; 388 389 current_token = current_token.append(&mut tiff_tree, next_ifd) 390 391 } else { 392 break; 393 } 394 } 395 */ 396 397 // look for known special IFDs on page 0 398 let page0_subifd = tiff_tree[page_0_token].data.get_tag(SUBFILE_TAG).copied(); 399 400 // grab SubIFDs for page 0 (DNG) 401 if let Some(subifd) = page0_subifd { 402 let decoded_offset = decode_offset(subifd.value_offset, ts.byte_order, ts.big_tiff)?; 403 input.seek(SeekFrom::Start(decoded_offset))?; 404 405 let num_longs = usize::value_from(subifd.value_count) 406 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; 407 let mut subfile_offsets = vec![0u32; num_longs]; // will contain offsets in native endianness 408 409 if num_longs * 4 <= 4 || ts.big_tiff && num_longs * 4 <= 8 { 410 let offset_bytes = subifd.value_offset.to_ne_bytes(); 411 let offset_reader = Cursor::new(offset_bytes); 412 413 with_order!(offset_reader, ts.byte_order, |src| { 414 for item in subfile_offsets.iter_mut().take(num_longs) { 415 let s = src.read_u32()?; // read a long from offset 416 *item = s; // write a long in output endian 417 } 418 }); 419 } else { 420 let mut buf = vec![0u8; num_longs * 4]; 421 input.read_exact(buf.as_mut_slice())?; 422 let offsets_buf = Cursor::new(buf); 423 424 with_order!(offsets_buf, ts.byte_order, |src| { 425 for item in subfile_offsets.iter_mut().take(num_longs) { 426 let s = src.read_u32()?; // read a long from offset 427 *item = s; // write a long in output endian 428 } 429 }); 430 } 431 432 // get all subfiles 433 for subfile_offset in subfile_offsets { 434 let u64_offset = u64::value_from(subfile_offset) 435 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; 436 input.seek(SeekFrom::Start(u64_offset))?; 437 438 //println!("Reading SubIFD: {}", u64_offset); 439 440 let subfile_ifd = 441 TiffStructure::read_ifd(input, ts.byte_order, ts.big_tiff, IfdType::Subfile)?; 442 let subfile_token = tiff_tree.new_node(subfile_ifd); 443 444 page_0_token 445 .append_node(&mut tiff_tree, subfile_token) 446 .map_err(|_err| Error::InvalidAsset("Bad TIFF Structure".to_string()))?; 447 } 448 } 449 450 // grab EXIF IFD for page 0 (DNG) 451 if let Some(exififd) = tiff_tree[page_0_token].data.get_tag(EXIFIFD_TAG) { 452 let decoded_offset = decode_offset(exififd.value_offset, ts.byte_order, ts.big_tiff)?; 453 input.seek(SeekFrom::Start(decoded_offset))?; 454 455 //println!("EXIF Reading SubIFD: {}", decoded_offset); 456 457 let exif_ifd = 458 TiffStructure::read_ifd(input, ts.byte_order, ts.big_tiff, IfdType::Exif)?; 459 let exif_token = tiff_tree.new_node(exif_ifd); 460 461 page_0_token 462 .append_node(&mut tiff_tree, exif_token) 463 .map_err(|_err| Error::InvalidAsset("Bad TIFF Structure".to_string()))?; 464 } 465 466 // grab GPS IFD for page 0 (DNG) 467 if let Some(gpsifd) = tiff_tree[page_0_token].data.get_tag(GPSIFD_TAG) { 468 let decoded_offset = decode_offset(gpsifd.value_offset, ts.byte_order, ts.big_tiff)?; 469 input.seek(SeekFrom::Start(decoded_offset))?; 470 471 //println!("GPS Reading SubIFD: {}", decoded_offset); 472 473 let gps_ifd = TiffStructure::read_ifd(input, ts.byte_order, ts.big_tiff, IfdType::Gps)?; 474 let gps_token = tiff_tree.new_node(gps_ifd); 475 476 page_0_token 477 .append_node(&mut tiff_tree, gps_token) 478 .map_err(|_err| Error::InvalidAsset("Bad TIFF Structure".to_string()))?; 479 } 480 481 (tiff_tree, page_0_token) 482 } else { 483 return Err(Error::InvalidAsset("TIFF structure invalid".to_string())); 484 }; 485 486 Ok((tiff_tree, page_0, ts.byte_order, ts.big_tiff)) 487 } 488 489 // struct used to clone source IFD entries. value_bytes are in target endianness 490 #[derive(Eq, PartialEq, Clone)] 491 pub(crate) struct IfdClonedEntry { 492 pub entry_tag: u16, 493 pub entry_type: u16, 494 pub value_count: u64, 495 pub value_bytes: Vec<u8>, 496 } 497 498 // struct to clone a TIFF/DNG and new tags if desired 499 pub(crate) struct TiffCloner<T> 500 where 501 T: Read + Write + Seek, 502 { 503 endianness: Endianness, 504 big_tiff: bool, 505 first_idf_offset: u64, 506 writer: ByteOrdered<T, Endianness>, 507 additional_ifds: BTreeMap<u16, IfdClonedEntry>, 508 } 509 510 impl<T: Read + Write + Seek> TiffCloner<T> { 511 pub fn new(endianness: Endianness, big_tiff: bool, writer: T) -> Result<TiffCloner<T>> { 512 let bo = ByteOrdered::runtime(writer, endianness); 513 514 let mut tc = TiffCloner { 515 endianness, 516 big_tiff, 517 first_idf_offset: 0, 518 writer: bo, 519 additional_ifds: BTreeMap::new(), 520 }; 521 522 tc.write_header()?; 523 524 Ok(tc) 525 } 526 527 fn offset(&mut self) -> Result<u64> { 528 Ok(self.writer.stream_position()?) 529 } 530 531 fn pad_word_boundary(&mut self) -> Result<()> { 532 let curr_offset = self.offset()?; 533 if curr_offset % 4 != 0 { 534 let padding = [0, 0, 0]; 535 let pad_len = 4 - (curr_offset % 4); 536 self.writer.write_all(&padding[..pad_len as usize])?; 537 } 538 539 Ok(()) 540 } 541 542 fn write_header(&mut self) -> Result<u64> { 543 let boi = match self.endianness { 544 Endianness::Big => 0x4d, 545 Endianness::Little => 0x49, 546 }; 547 let offset; 548 549 if self.big_tiff { 550 self.writer.write_all(&[boi, boi])?; 551 self.writer.write_u16(43u16)?; 552 self.writer.write_u16(8u16)?; 553 self.writer.write_u16(0u16)?; 554 offset = self.writer.stream_position()?; // first ifd offset 555 556 self.writer.write_u64(0)?; 557 } else { 558 self.writer.write_all(&[boi, boi])?; 559 self.writer.write_u16(42u16)?; 560 offset = self.writer.stream_position()?; // first ifd offset 561 562 self.writer.write_u32(0)?; 563 } 564 565 self.first_idf_offset = offset; 566 Ok(offset) 567 } 568 569 fn write_entry_count(&mut self, count: usize) -> Result<()> { 570 if self.big_tiff { 571 let cnt = u64::value_from(count) 572 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; // get beginning of chunk which starts 4 bytes before label 573 574 self.writer.write_u64(cnt)?; 575 } else { 576 let cnt = u16::value_from(count) 577 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; // get beginning of chunk which starts 4 bytes before label 578 579 self.writer.write_u16(cnt)?; 580 } 581 582 Ok(()) 583 } 584 585 fn write_ifd(&mut self, target_ifd: &mut BTreeMap<u16, IfdClonedEntry>) -> Result<u64> { 586 // write out all data and save the offsets, skipping subfiles since the data is already written 587 for &mut IfdClonedEntry { 588 value_bytes: ref mut value_bytes_ref, 589 .. 590 } in target_ifd.values_mut() 591 { 592 let data_bytes = if self.big_tiff { 8 } else { 4 }; 593 594 if value_bytes_ref.len() > data_bytes { 595 // get location of entry data start 596 let offset = self.writer.stream_position()?; 597 598 // write out the data bytes 599 self.writer.write_all(value_bytes_ref)?; 600 601 // set offset pointer in file source endian 602 let mut offset_vec = vec![0; data_bytes]; 603 604 with_order!(offset_vec.as_mut_slice(), self.endianness, |ew| { 605 if self.big_tiff { 606 ew.write_u64(offset)?; 607 } else { 608 let offset_u32 = u32::value_from(offset).map_err(|_err| { 609 Error::InvalidAsset("value out of range".to_string()) 610 })?; // get beginning of chunk which starts 4 bytes before label 611 612 ew.write_u32(offset_u32)?; 613 } 614 }); 615 616 // set to new data offset position 617 *value_bytes_ref = offset_vec; 618 } else { 619 while value_bytes_ref.len() < data_bytes { 620 value_bytes_ref.push(0); 621 } 622 } 623 } 624 625 // Write out the IFD 626 627 // start on a WORD boundary 628 self.pad_word_boundary()?; 629 630 // save location of start of IFD 631 let ifd_offset = self.writer.stream_position()?; 632 633 // write out the entry count 634 self.write_entry_count(target_ifd.len())?; 635 636 // write out the directory entries 637 for (tag, entry) in target_ifd.iter() { 638 self.writer.write_u16(*tag)?; 639 self.writer.write_u16(entry.entry_type)?; 640 641 if self.big_tiff { 642 let cnt = u64::value_from(entry.value_count) 643 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; 644 645 self.writer.write_u64(cnt)?; 646 } else { 647 let cnt = u32::value_from(entry.value_count) 648 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; 649 650 self.writer.write_u32(cnt)?; 651 } 652 653 self.writer.write_all(&entry.value_bytes)?; 654 } 655 656 Ok(ifd_offset) 657 } 658 659 // add new TAG by supplying the IDF entry 660 pub fn add_target_tag(&mut self, entry: IfdClonedEntry) { 661 self.additional_ifds.insert(entry.entry_tag, entry); 662 } 663 664 fn clone_image_data<R: Read + Seek + ?Sized>( 665 &mut self, 666 target_ifd: &mut BTreeMap<u16, IfdClonedEntry>, 667 asset_reader: &mut R, 668 ) -> Result<()> { 669 match ( 670 target_ifd.contains_key(&STRIPBYTECOUNTS), 671 target_ifd.contains_key(&STRIPOFFSETS), 672 target_ifd.contains_key(&TILEBYTECOUNTS), 673 target_ifd.contains_key(&TILEOFFSETS), 674 ) { 675 (true, true, false, false) => { 676 // stripped image data 677 let sbc_entry = target_ifd[&STRIPBYTECOUNTS].clone(); 678 let so_entry = target_ifd.get_mut(&STRIPOFFSETS).ok_or(Error::NotFound)?; 679 680 // check for well formed TIFF 681 if so_entry.value_count != sbc_entry.value_count { 682 return Err(Error::InvalidAsset( 683 "TIFF strip count does not match strip offset count".to_string(), 684 )); 685 } 686 687 let mut sbcs = vec![0u64; sbc_entry.value_count as usize]; 688 let mut dest_offsets: Vec<u64> = Vec::new(); 689 690 // get the byte counts 691 with_order!(sbc_entry.value_bytes.as_slice(), self.endianness, |src| { 692 for c in &mut sbcs { 693 match sbc_entry.entry_type { 694 4u16 => { 695 let s = src.read_u32()?; 696 *c = s.into(); 697 } 698 3u16 => { 699 let s = src.read_u16()?; 700 *c = s.into(); 701 } 702 16u16 => { 703 let s = src.read_u64()?; 704 *c = s; 705 } 706 _ => return Err(Error::InvalidAsset("invalid TIFF strip".to_string())), 707 } 708 } 709 }); 710 711 // seek to end of file 712 self.writer.seek(SeekFrom::End(0))?; 713 714 // copy the strips 715 with_order!(so_entry.value_bytes.as_slice(), self.endianness, |src| { 716 for c in sbcs.iter() { 717 let cnt = usize::value_from(*c).map_err(|_err| { 718 Error::InvalidAsset("value out of range".to_string()) 719 })?; 720 721 // get the offset 722 let so: u64 = match so_entry.entry_type { 723 4u16 => { 724 let s = src.read_u32()?; 725 s.into() 726 } 727 3u16 => { 728 let s = src.read_u16()?; 729 s.into() 730 } 731 16u16 => src.read_u64()?, 732 _ => return Err(Error::InvalidAsset("invalid TIFF strip".to_string())), 733 }; 734 735 let dest_offset = self.writer.stream_position()?; 736 dest_offsets.push(dest_offset); 737 738 // copy the strip to new file 739 let mut data = vec![0u8; cnt]; 740 asset_reader.seek(SeekFrom::Start(so))?; 741 asset_reader.read_exact(data.as_mut_slice())?; 742 self.writer.write_all(data.as_slice())?; 743 } 744 }); 745 746 // patch the offsets 747 with_order!( 748 so_entry.value_bytes.as_mut_slice(), 749 self.endianness, 750 |dest| { 751 for o in dest_offsets.iter() { 752 // get the offset 753 match so_entry.entry_type { 754 4u16 => { 755 let offset = u32::value_from(*o).map_err(|_err| { 756 Error::InvalidAsset("value out of range".to_string()) 757 })?; 758 dest.write_u32(offset)?; 759 } 760 3u16 => { 761 let offset = u16::value_from(*o).map_err(|_err| { 762 Error::InvalidAsset("value out of range".to_string()) 763 })?; 764 dest.write_u16(offset)?; 765 } 766 16u16 => { 767 let offset = *o; 768 dest.write_u64(offset)?; 769 } 770 _ => { 771 return Err(Error::InvalidAsset( 772 "invalid TIFF strip".to_string(), 773 )) 774 } 775 } 776 } 777 } 778 ); 779 } 780 (false, false, true, true) => { 781 // tiled image data 782 let tbc_entry = target_ifd[&TILEBYTECOUNTS].clone(); 783 let to_entry = target_ifd.get_mut(&TILEOFFSETS).ok_or(Error::NotFound)?; 784 785 // check for well formed TIFF 786 if to_entry.value_count != tbc_entry.value_count { 787 return Err(Error::InvalidAsset( 788 "TIFF tile count does not match tile offset count".to_string(), 789 )); 790 } 791 792 let mut tbcs = vec![0u64; tbc_entry.value_count as usize]; 793 let mut dest_offsets: Vec<u64> = Vec::new(); 794 795 // get the byte counts 796 with_order!(tbc_entry.value_bytes.as_slice(), self.endianness, |src| { 797 for val in &mut tbcs { 798 match tbc_entry.entry_type { 799 4u16 => { 800 let s = src.read_u32()?; 801 *val = s.into(); 802 } 803 3u16 => { 804 let s = src.read_u16()?; 805 *val = s.into(); 806 } 807 16u16 => { 808 let s = src.read_u64()?; 809 *val = s; 810 } 811 _ => return Err(Error::InvalidAsset("invalid TIFF tile".to_string())), 812 } 813 } 814 }); 815 816 // seek to end of file 817 self.writer.seek(SeekFrom::End(0))?; 818 819 // copy the tiles 820 with_order!(to_entry.value_bytes.as_slice(), self.endianness, |src| { 821 for c in tbcs.iter() { 822 let cnt = usize::value_from(*c).map_err(|_err| { 823 Error::InvalidAsset("value out of range".to_string()) 824 })?; 825 826 // get the offset 827 let to: u64 = match to_entry.entry_type { 828 4u16 => { 829 let s = src.read_u32()?; 830 s.into() 831 } 832 16u16 => src.read_u64()?, 833 _ => return Err(Error::InvalidAsset("invalid TIFF tile".to_string())), 834 }; 835 836 let dest_offset = self.writer.stream_position()?; 837 dest_offsets.push(dest_offset); 838 839 // copy the tile to new file 840 let mut data = vec![0u8; cnt]; 841 asset_reader.seek(SeekFrom::Start(to))?; 842 asset_reader.read_exact(data.as_mut_slice())?; 843 self.writer.write_all(data.as_slice())?; 844 } 845 }); 846 847 // patch the offsets 848 with_order!( 849 to_entry.value_bytes.as_mut_slice(), 850 self.endianness, 851 |dest| { 852 for v in dest_offsets.iter() { 853 // get the offset 854 match to_entry.entry_type { 855 4u16 => { 856 let offset = u32::value_from(*v).map_err(|_err| { 857 Error::InvalidAsset("value out of range".to_string()) 858 })?; 859 dest.write_u32(offset)?; 860 } 861 3u16 => { 862 let offset = u16::value_from(*v).map_err(|_err| { 863 Error::InvalidAsset("value out of range".to_string()) 864 })?; 865 dest.write_u16(offset)?; 866 } 867 16u16 => { 868 let offset = *v; 869 dest.write_u64(offset)?; 870 } 871 _ => { 872 return Err(Error::InvalidAsset( 873 "invalid TIFF tile".to_string(), 874 )) 875 } 876 } 877 } 878 } 879 ); 880 } 881 (_, _, _, _) => (), 882 }; 883 884 Ok(()) 885 } 886 887 fn clone_sub_files<R: Read + Seek + ?Sized>( 888 &mut self, 889 tiff_tree: &Arena<ImageFileDirectory>, 890 page: Token, 891 asset_reader: &mut R, 892 ) -> Result<HashMap<u16, Vec<u64>>> { 893 // offset map 894 let mut offset_map: HashMap<u16, Vec<u64>> = HashMap::new(); 895 896 let mut offsets_ifd: Vec<u64> = Vec::new(); 897 let mut offsets_exif: Vec<u64> = Vec::new(); 898 let mut offsets_gps: Vec<u64> = Vec::new(); 899 900 // clone the EXIF entry and DNG entries 901 for n in page.children(tiff_tree) { 902 let ifd = &n.data; 903 904 // clone IFD entries 905 let mut cloned_ifd = self.clone_ifd_entries(&ifd.entries, asset_reader)?; 906 907 // clone the image data 908 self.clone_image_data(&mut cloned_ifd, asset_reader)?; 909 910 // write directory 911 let sub_ifd_offset = self.write_ifd(&mut cloned_ifd)?; 912 913 // terminate since we don't support chained subifd 914 if self.big_tiff { 915 self.writer.write_u64(0)?; 916 } else { 917 self.writer.write_u32(0)?; 918 } 919 920 // fix up offset in main page known IFDs 921 match ifd.ifd_type { 922 IfdType::Page => (), 923 IfdType::Subfile => offsets_ifd.push(sub_ifd_offset), 924 IfdType::Exif => offsets_exif.push(sub_ifd_offset), 925 IfdType::Gps => offsets_gps.push(sub_ifd_offset), 926 }; 927 } 928 929 offset_map.insert(SUBFILE_TAG, offsets_ifd); 930 offset_map.insert(EXIFIFD_TAG, offsets_exif); 931 offset_map.insert(GPSIFD_TAG, offsets_gps); 932 933 Ok(offset_map) 934 } 935 936 pub fn clone_tiff<R: Read + Seek + ?Sized>( 937 &mut self, 938 tiff_tree: &mut Arena<ImageFileDirectory>, 939 page_0: Token, 940 asset_reader: &mut R, 941 ) -> Result<()> { 942 // handle page 0 943 944 // clone the subfile entries (DNG) 945 let subfile_offsets = self.clone_sub_files(tiff_tree, page_0, asset_reader)?; 946 947 let page_0_idf = tiff_tree 948 .get_mut(page_0) 949 .ok_or_else(|| Error::InvalidAsset("TIFF does not have IFD".to_string()))?; 950 951 // clone IFD entries 952 let mut cloned_ifd = self.clone_ifd_entries(&page_0_idf.data.entries, asset_reader)?; 953 954 // clone the image data 955 self.clone_image_data(&mut cloned_ifd, asset_reader)?; 956 957 // add in new Tags 958 for (tag, new_entry) in &self.additional_ifds { 959 cloned_ifd.insert(*tag, new_entry.clone()); 960 } 961 962 // fix up subfile offsets 963 for t in SUBFILES { 964 if let Some(offsets) = subfile_offsets.get(&t) { 965 if offsets.is_empty() { 966 continue; 967 } 968 969 let e = cloned_ifd 970 .get_mut(&t) 971 .ok_or_else(|| Error::InvalidAsset("TIFF does not have IFD".to_string()))?; 972 let mut adjust_offsets = if self.big_tiff { 973 vec![0u8; offsets.len() * 8] 974 } else { 975 vec![0u8; offsets.len() * 4] 976 }; 977 978 with_order!(adjust_offsets.as_mut_slice(), self.endianness, |dest| { 979 for o in offsets { 980 if self.big_tiff { 981 dest.write_u64(*o)?; 982 } else { 983 let offset_u32 = u32::value_from(*o).map_err(|_err| { 984 Error::InvalidAsset("value out of range".to_string()) 985 })?; 986 987 dest.write_u32(offset_u32)?; 988 } 989 } 990 }); 991 992 e.value_bytes = adjust_offsets; 993 } 994 } 995 996 // write directory 997 let first_ifd_offset = self.write_ifd(&mut cloned_ifd)?; 998 999 // write final location info 1000 let curr_pos = self.offset()?; 1001 1002 self.writer.seek(SeekFrom::Start(self.first_idf_offset))?; 1003 1004 if self.big_tiff { 1005 self.writer.write_u64(first_ifd_offset)?; 1006 self.writer.seek(SeekFrom::Start(curr_pos))?; 1007 self.writer.write_u64(0)?; 1008 } else { 1009 let offset_u32 = u32::value_from(first_ifd_offset) 1010 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; // get beginning of chunk which starts 4 bytes before label 1011 1012 self.writer.write_u32(offset_u32)?; 1013 self.writer.seek(SeekFrom::Start(curr_pos))?; 1014 self.writer.write_u32(0)?; 1015 } 1016 self.writer.flush()?; 1017 Ok(()) 1018 } 1019 1020 fn clone_ifd_entries<R: Read + Seek + ?Sized>( 1021 &mut self, 1022 entries: &HashMap<u16, IfdEntry>, 1023 asset_reader: &mut R, 1024 ) -> Result<BTreeMap<u16, IfdClonedEntry>> { 1025 let mut target_ifd: BTreeMap<u16, IfdClonedEntry> = BTreeMap::new(); 1026 1027 for (tag, entry) in entries { 1028 let target_endianness = self.writer.endianness(); 1029 1030 // get bytes for tag 1031 let cnt = entry.value_count; 1032 let et = entry.entry_type; 1033 1034 let entry_type = IFDEntryType::from_u16(et).ok_or(Error::UnsupportedType)?; 1035 1036 // read IFD raw data in file native endian format 1037 let data = match entry_type { 1038 IFDEntryType::Byte 1039 | IFDEntryType::Sbyte 1040 | IFDEntryType::Undefined 1041 | IFDEntryType::Ascii => { 1042 let num_bytes = usize::value_from(cnt) 1043 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; 1044 1045 let mut data = vec![0u8; num_bytes]; 1046 1047 if num_bytes <= 4 || self.big_tiff && num_bytes <= 8 { 1048 let offset_bytes = entry.value_offset.to_ne_bytes(); 1049 for (i, item) in offset_bytes.iter().take(num_bytes).enumerate() { 1050 data[i] = *item; 1051 } 1052 } else { 1053 // move to start of data 1054 asset_reader.seek(SeekFrom::Start(decode_offset( 1055 entry.value_offset, 1056 target_endianness, 1057 self.big_tiff, 1058 )?))?; 1059 asset_reader.read_exact(data.as_mut_slice())?; 1060 } 1061 1062 data 1063 } 1064 IFDEntryType::Short => { 1065 let num_shorts = usize::value_from(cnt) 1066 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; 1067 let mut data = vec![0u8; num_shorts * 2]; 1068 1069 if num_shorts * 2 <= 4 || self.big_tiff && num_shorts * 2 <= 8 { 1070 let offset_bytes = entry.value_offset.to_ne_bytes(); 1071 let mut offset_reader = Cursor::new(offset_bytes); 1072 1073 let mut w = Cursor::new(data.as_mut_slice()); 1074 for _i in 0..num_shorts { 1075 let s = offset_reader.read_u16::<NativeEndian>()?; // read a short from offset 1076 w.write_u16::<NativeEndian>(s)?; // write a short in output endian 1077 } 1078 } else { 1079 // move to start of data 1080 asset_reader.seek(SeekFrom::Start(decode_offset( 1081 entry.value_offset, 1082 target_endianness, 1083 self.big_tiff, 1084 )?))?; 1085 asset_reader.read_exact(data.as_mut_slice())?; 1086 } 1087 1088 data 1089 } 1090 IFDEntryType::Long | IFDEntryType::Ifd => { 1091 let num_longs = usize::value_from(cnt) 1092 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; 1093 let mut data = vec![0u8; num_longs * 4]; 1094 1095 if num_longs * 4 <= 4 || self.big_tiff && num_longs * 4 <= 8 { 1096 let offset_bytes = entry.value_offset.to_ne_bytes(); 1097 let mut offset_reader = Cursor::new(offset_bytes); 1098 1099 let mut w = Cursor::new(data.as_mut_slice()); 1100 for _i in 0..num_longs { 1101 let s = offset_reader.read_u32::<NativeEndian>()?; // read a long from offset 1102 w.write_u32::<NativeEndian>(s)?; // write a long in output endian 1103 } 1104 } else { 1105 // move to start of data 1106 asset_reader.seek(SeekFrom::Start(decode_offset( 1107 entry.value_offset, 1108 target_endianness, 1109 self.big_tiff, 1110 )?))?; 1111 asset_reader.read_exact(data.as_mut_slice())?; 1112 } 1113 1114 data 1115 } 1116 IFDEntryType::Rational => { 1117 let num_rationals = usize::value_from(cnt) 1118 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; 1119 let mut data = vec![0u8; num_rationals * 8]; 1120 1121 // move to start of data 1122 asset_reader.seek(SeekFrom::Start(decode_offset( 1123 entry.value_offset, 1124 target_endianness, 1125 self.big_tiff, 1126 )?))?; 1127 1128 asset_reader.read_exact(data.as_mut_slice())?; 1129 1130 data 1131 } 1132 IFDEntryType::Sshort => { 1133 let num_sshorts = usize::value_from(cnt) 1134 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; 1135 let mut data = vec![0u8; num_sshorts * 2]; 1136 1137 if num_sshorts * 2 <= 4 || self.big_tiff && num_sshorts * 2 <= 8 { 1138 let offset_bytes = entry.value_offset.to_ne_bytes(); 1139 let mut offset_reader = Cursor::new(offset_bytes); 1140 1141 let mut w = Cursor::new(data.as_mut_slice()); 1142 for _i in 0..num_sshorts { 1143 let s = offset_reader.read_i16::<NativeEndian>()?; // read a short from offset 1144 w.write_i16::<NativeEndian>(s)?; // write a short in output endian 1145 } 1146 } else { 1147 // move to start of data 1148 asset_reader.seek(SeekFrom::Start(decode_offset( 1149 entry.value_offset, 1150 target_endianness, 1151 self.big_tiff, 1152 )?))?; 1153 asset_reader.read_exact(data.as_mut_slice())?; 1154 } 1155 1156 data 1157 } 1158 IFDEntryType::Slong => { 1159 let num_slongs = usize::value_from(cnt) 1160 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; 1161 let mut data = vec![0u8; num_slongs * 4]; 1162 1163 if num_slongs * 4 <= 4 || self.big_tiff && num_slongs * 4 <= 8 { 1164 let offset_bytes = entry.value_offset.to_ne_bytes(); 1165 let mut offset_reader = Cursor::new(offset_bytes); 1166 1167 let mut w = Cursor::new(data.as_mut_slice()); 1168 for _i in 0..num_slongs { 1169 let s = offset_reader.read_i32::<NativeEndian>()?; // read a slong from offset 1170 w.write_i32::<NativeEndian>(s)?; // write a slong in output endian 1171 } 1172 } else { 1173 // move to start of data 1174 asset_reader.seek(SeekFrom::Start(decode_offset( 1175 entry.value_offset, 1176 target_endianness, 1177 self.big_tiff, 1178 )?))?; 1179 asset_reader.read_exact(data.as_mut_slice())?; 1180 } 1181 1182 data 1183 } 1184 IFDEntryType::Srational => { 1185 let num_srationals = usize::value_from(cnt) 1186 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; 1187 let mut data = vec![0u8; num_srationals * 8]; 1188 1189 // move to start of data 1190 asset_reader.seek(SeekFrom::Start(decode_offset( 1191 entry.value_offset, 1192 target_endianness, 1193 self.big_tiff, 1194 )?))?; 1195 asset_reader.read_exact(data.as_mut_slice())?; 1196 1197 data 1198 } 1199 IFDEntryType::Float => { 1200 let num_floats = usize::value_from(cnt) 1201 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; 1202 let mut data = vec![0u8; num_floats * 4]; 1203 1204 if num_floats * 4 <= 4 || self.big_tiff && num_floats * 4 <= 8 { 1205 let offset_bytes = entry.value_offset.to_ne_bytes(); 1206 let mut offset_reader = Cursor::new(offset_bytes); 1207 1208 let mut w = Cursor::new(data.as_mut_slice()); 1209 for _i in 0..num_floats { 1210 let s = offset_reader.read_f32::<NativeEndian>()?; // read a float from offset 1211 w.write_f32::<NativeEndian>(s)?; // write a float in output endian 1212 } 1213 } else { 1214 // move to start of data 1215 asset_reader.seek(SeekFrom::Start(decode_offset( 1216 entry.value_offset, 1217 target_endianness, 1218 self.big_tiff, 1219 )?))?; 1220 asset_reader.read_exact(data.as_mut_slice())?; 1221 } 1222 1223 data 1224 } 1225 IFDEntryType::Double => { 1226 let num_doubles = usize::value_from(cnt) 1227 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; 1228 let mut data = vec![0u8; num_doubles * 8]; 1229 1230 // move to start of data 1231 asset_reader.seek(SeekFrom::Start(decode_offset( 1232 entry.value_offset, 1233 target_endianness, 1234 self.big_tiff, 1235 )?))?; 1236 asset_reader.read_exact(data.as_mut_slice())?; 1237 1238 data 1239 } 1240 IFDEntryType::Long8 | IFDEntryType::Ifd8 => { 1241 let num_long8s = usize::value_from(cnt) 1242 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; 1243 let mut data = vec![0u8; num_long8s * 8]; 1244 1245 // move to start of data 1246 asset_reader.seek(SeekFrom::Start(decode_offset( 1247 entry.value_offset, 1248 target_endianness, 1249 self.big_tiff, 1250 )?))?; 1251 asset_reader.read_exact(data.as_mut_slice())?; 1252 1253 data 1254 } 1255 IFDEntryType::Slong8 => { 1256 let num_slong8s = usize::value_from(cnt) 1257 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; 1258 let mut data = vec![0u8; num_slong8s * 8]; 1259 1260 // move to start of data 1261 asset_reader.seek(SeekFrom::Start(decode_offset( 1262 entry.value_offset, 1263 target_endianness, 1264 self.big_tiff, 1265 )?))?; 1266 asset_reader.read_exact(data.as_mut_slice())?; 1267 1268 data 1269 } 1270 }; 1271 1272 target_ifd.insert( 1273 *tag, 1274 IfdClonedEntry { 1275 entry_tag: *tag, 1276 entry_type: entry_type as u16, 1277 value_count: cnt, 1278 value_bytes: data, 1279 }, 1280 ); 1281 } 1282 1283 Ok(target_ifd) 1284 } 1285 } 1286 1287 fn tiff_clone_with_tags<R: Read + Seek + ?Sized, W: Read + Write + Seek + ?Sized>( 1288 writer: &mut W, 1289 asset_reader: &mut R, 1290 tiff_tags: Vec<IfdClonedEntry>, 1291 ) -> Result<()> { 1292 let (mut tiff_tree, page_0, endianness, big_tiff) = map_tiff(asset_reader)?; 1293 1294 let mut bo = ByteOrdered::new(writer, endianness); 1295 1296 let mut tc = TiffCloner::new(endianness, big_tiff, &mut bo)?; 1297 1298 for t in tiff_tags { 1299 tc.add_target_tag(t); 1300 } 1301 1302 tc.clone_tiff(&mut tiff_tree, page_0, asset_reader)?; 1303 1304 Ok(()) 1305 } 1306 fn add_required_tags_to_stream( 1307 input_stream: &mut dyn CAIRead, 1308 output_stream: &mut dyn CAIReadWrite, 1309 ) -> Result<()> { 1310 let tiff_io = TiffIO {}; 1311 1312 match tiff_io.read_cai(input_stream) { 1313 Ok(_) => { 1314 // just clone 1315 input_stream.rewind()?; 1316 output_stream.rewind()?; 1317 std::io::copy(input_stream, output_stream)?; 1318 Ok(()) 1319 } 1320 Err(Error::JumbfNotFound) => { 1321 // allocate enough bytes so that value is not stored in offset field 1322 let some_bytes = vec![0u8; 10]; 1323 let tio = TiffIO {}; 1324 tio.write_cai(input_stream, output_stream, &some_bytes) 1325 } 1326 Err(e) => Err(e), 1327 } 1328 } 1329 1330 fn get_cai_data<R>(asset_reader: &mut R) -> Result<Vec<u8>> 1331 where 1332 R: Read + Seek + ?Sized, 1333 { 1334 let (tiff_tree, page_0, e, big_tiff) = map_tiff(asset_reader)?; 1335 1336 let first_ifd = &tiff_tree[page_0].data; 1337 1338 let cai_ifd_entry = first_ifd.get_tag(C2PA_TAG).ok_or(Error::JumbfNotFound)?; 1339 1340 // make sure data type is for unstructured data 1341 if cai_ifd_entry.entry_type != C2PA_FIELD_TYPE { 1342 return Err(Error::InvalidAsset( 1343 "Ifd entry for C2PA must be type UNDEFINED(7)".to_string(), 1344 )); 1345 } 1346 1347 // move read point to start of entry 1348 let decoded_offset = decode_offset(cai_ifd_entry.value_offset, e, big_tiff)?; 1349 asset_reader.seek(SeekFrom::Start(decoded_offset))?; 1350 1351 let manifest_len: usize = usize::value_from(cai_ifd_entry.value_count) 1352 .map_err(|_err| Error::InvalidAsset("TIFF/DNG out of range".to_string()))?; 1353 1354 let mut data = vec![0u8; manifest_len]; 1355 1356 asset_reader 1357 .read_exact(data.as_mut_slice()) 1358 .map_err(|_err| Error::InvalidAsset("TIFF/DNG out of range".to_string()))?; 1359 1360 Ok(data) 1361 } 1362 1363 fn get_xmp_data<R>(asset_reader: &mut R) -> Option<Vec<u8>> 1364 where 1365 R: Read + Seek + ?Sized, 1366 { 1367 let (tiff_tree, page_0, e, big_tiff) = map_tiff(asset_reader).ok()?; 1368 let first_ifd = &tiff_tree[page_0].data; 1369 1370 let xmp_ifd_entry = match first_ifd.get_tag(XMP_TAG) { 1371 Some(entry) => entry, 1372 None => return None, 1373 }; 1374 1375 // make sure the tag type is correct 1376 if IFDEntryType::from_u16(xmp_ifd_entry.entry_type)? != IFDEntryType::Byte { 1377 return None; 1378 } 1379 1380 // move read point to start of entry 1381 let decoded_offset = decode_offset(xmp_ifd_entry.value_offset, e, big_tiff).ok()?; 1382 asset_reader.seek(SeekFrom::Start(decoded_offset)).ok()?; 1383 1384 let xmp_len: usize = usize::value_from(xmp_ifd_entry.value_count).ok()?; 1385 1386 let mut data = vec![0u8; xmp_len]; 1387 1388 asset_reader.read_exact(data.as_mut_slice()).ok()?; 1389 1390 Some(data) 1391 } 1392 pub struct TiffIO {} 1393 1394 impl CAIReader for TiffIO { 1395 fn read_cai(&self, asset_reader: &mut dyn CAIRead) -> Result<Vec<u8>> { 1396 let cai_data = get_cai_data(asset_reader)?; 1397 Ok(cai_data) 1398 } 1399 1400 fn read_xmp(&self, asset_reader: &mut dyn CAIRead) -> Option<String> { 1401 let xmp_data = get_xmp_data(asset_reader)?; 1402 String::from_utf8(xmp_data).ok() 1403 } 1404 } 1405 1406 impl AssetIO for TiffIO { 1407 fn asset_patch_ref(&self) -> Option<&dyn AssetPatch> { 1408 Some(self) 1409 } 1410 1411 fn read_cai_store(&self, asset_path: &std::path::Path) -> Result<Vec<u8>> { 1412 let mut reader = std::fs::File::open(asset_path)?; 1413 1414 self.read_cai(&mut reader) 1415 } 1416 1417 fn save_cai_store(&self, asset_path: &std::path::Path, store_bytes: &[u8]) -> Result<()> { 1418 let mut input_stream = std::fs::OpenOptions::new() 1419 .read(true) 1420 .open(asset_path) 1421 .map_err(Error::IoError)?; 1422 1423 let mut temp_file = Builder::new() 1424 .prefix("c2pa_temp") 1425 .rand_bytes(5) 1426 .tempfile()?; 1427 1428 self.write_cai(&mut input_stream, &mut temp_file, store_bytes)?; 1429 1430 // copy temp file to asset 1431 rename_or_copy(temp_file, asset_path) 1432 } 1433 1434 fn get_object_locations( 1435 &self, 1436 asset_path: &std::path::Path, 1437 ) -> Result<Vec<crate::asset_io::HashObjectPositions>> { 1438 let mut input_stream = 1439 std::fs::File::open(asset_path).map_err(|_err| Error::EmbeddingError)?; 1440 1441 self.get_object_locations_from_stream(&mut input_stream) 1442 } 1443 1444 fn remove_cai_store(&self, asset_path: &std::path::Path) -> Result<()> { 1445 let mut input_file = std::fs::File::open(asset_path)?; 1446 1447 let mut temp_file = Builder::new() 1448 .prefix("c2pa_temp") 1449 .rand_bytes(5) 1450 .tempfile()?; 1451 1452 self.remove_cai_store_from_stream(&mut input_file, &mut temp_file)?; 1453 1454 // copy temp file to asset 1455 rename_or_copy(temp_file, asset_path) 1456 } 1457 1458 fn new(_asset_type: &str) -> Self 1459 where 1460 Self: Sized, 1461 { 1462 TiffIO {} 1463 } 1464 1465 fn get_handler(&self, asset_type: &str) -> Box<dyn AssetIO> { 1466 Box::new(TiffIO::new(asset_type)) 1467 } 1468 1469 fn get_reader(&self) -> &dyn CAIReader { 1470 self 1471 } 1472 1473 fn get_writer(&self, asset_type: &str) -> Option<Box<dyn CAIWriter>> { 1474 Some(Box::new(TiffIO::new(asset_type))) 1475 } 1476 1477 fn remote_ref_writer_ref(&self) -> Option<&dyn RemoteRefEmbed> { 1478 Some(self) 1479 } 1480 1481 fn composed_data_ref(&self) -> Option<&dyn ComposedManifestRef> { 1482 Some(self) 1483 } 1484 1485 fn supported_types(&self) -> &[&str] { 1486 &SUPPORTED_TYPES 1487 } 1488 } 1489 1490 impl CAIWriter for TiffIO { 1491 fn write_cai( 1492 &self, 1493 input_stream: &mut dyn CAIRead, 1494 output_stream: &mut dyn CAIReadWrite, 1495 store_bytes: &[u8], 1496 ) -> Result<()> { 1497 let l = u64::value_from(store_bytes.len()) 1498 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; 1499 1500 let entry = IfdClonedEntry { 1501 entry_tag: C2PA_TAG, 1502 entry_type: C2PA_FIELD_TYPE, 1503 value_count: l, 1504 value_bytes: store_bytes.to_vec(), 1505 }; 1506 1507 tiff_clone_with_tags(output_stream, input_stream, vec![entry]) 1508 } 1509 1510 fn get_object_locations_from_stream( 1511 &self, 1512 input_stream: &mut dyn CAIRead, 1513 ) -> Result<Vec<HashObjectPositions>> { 1514 let len = stream_len(input_stream)?; 1515 let vec_cap = usize::value_from(len) 1516 .map_err(|_err| Error::InvalidAsset("value out of range".to_owned()))?; 1517 let output_buf: Vec<u8> = Vec::with_capacity(vec_cap + 100); 1518 1519 let mut output_stream = Cursor::new(output_buf); 1520 1521 add_required_tags_to_stream(input_stream, &mut output_stream)?; 1522 output_stream.rewind()?; 1523 1524 let (idfs, first_idf_token, e, big_tiff) = map_tiff(&mut output_stream)?; 1525 1526 let cai_ifd_entry = match idfs[first_idf_token].data.get_tag(C2PA_TAG) { 1527 Some(ifd) => ifd, 1528 None => return Ok(Vec::new()), 1529 }; 1530 1531 // make sure data type is for unstructured data 1532 if cai_ifd_entry.entry_type != C2PA_FIELD_TYPE { 1533 return Err(Error::InvalidAsset( 1534 "Ifd entry for C2PA must be type UNKNOWN(7)".to_string(), 1535 )); 1536 } 1537 1538 let decoded_offset = decode_offset(cai_ifd_entry.value_offset, e, big_tiff)?; 1539 let manifest_offset = usize::value_from(decoded_offset) 1540 .map_err(|_err| Error::InvalidAsset("TIFF/DNG out of range".to_string()))?; 1541 let manifest_len = usize::value_from(cai_ifd_entry.value_count) 1542 .map_err(|_err| Error::InvalidAsset("TIFF/DNG out of range".to_string()))?; 1543 1544 Ok(vec![HashObjectPositions { 1545 offset: manifest_offset, 1546 length: manifest_len, 1547 htype: HashBlockObjectType::Cai, 1548 }]) 1549 } 1550 1551 fn remove_cai_store_from_stream( 1552 &self, 1553 input_stream: &mut dyn CAIRead, 1554 output_stream: &mut dyn CAIReadWrite, 1555 ) -> Result<()> { 1556 let (mut idfs, page_0, e, big_tiff) = map_tiff(input_stream)?; 1557 1558 let mut bo = ByteOrdered::new(output_stream, e); 1559 let mut tc = TiffCloner::new(e, big_tiff, &mut bo)?; 1560 1561 idfs[page_0].data.entries.remove(&C2PA_TAG); 1562 tc.clone_tiff(&mut idfs, page_0, input_stream)?; 1563 Ok(()) 1564 } 1565 } 1566 1567 impl AssetPatch for TiffIO { 1568 fn patch_cai_store(&self, asset_path: &std::path::Path, store_bytes: &[u8]) -> Result<()> { 1569 let mut asset_io = OpenOptions::new() 1570 .write(true) 1571 .read(true) 1572 .create(false) 1573 .open(asset_path)?; 1574 1575 let (tiff_tree, page_0, e, big_tiff) = map_tiff(&mut asset_io)?; 1576 1577 let first_ifd = &tiff_tree[page_0].data; 1578 1579 let cai_ifd_entry = first_ifd.get_tag(C2PA_TAG).ok_or(Error::JumbfNotFound)?; 1580 1581 // make sure data type is for unstructured data 1582 if cai_ifd_entry.entry_type != C2PA_FIELD_TYPE { 1583 return Err(Error::InvalidAsset( 1584 "Ifd entry for C2PA must be type UNKNOWN(7)".to_string(), 1585 )); 1586 } 1587 1588 let manifest_len: usize = usize::value_from(cai_ifd_entry.value_count) 1589 .map_err(|_err| Error::InvalidAsset("TIFF/DNG out of range".to_string()))?; 1590 1591 if store_bytes.len() == manifest_len { 1592 // move read point to start of entry 1593 let decoded_offset = decode_offset(cai_ifd_entry.value_offset, e, big_tiff)?; 1594 asset_io.seek(SeekFrom::Start(decoded_offset))?; 1595 1596 asset_io.write_all(store_bytes)?; 1597 Ok(()) 1598 } else { 1599 Err(Error::InvalidAsset( 1600 "patch_cai_store store size mismatch.".to_string(), 1601 )) 1602 } 1603 } 1604 } 1605 1606 impl RemoteRefEmbed for TiffIO { 1607 #[allow(unused_variables)] 1608 fn embed_reference( 1609 &self, 1610 asset_path: &Path, 1611 embed_ref: crate::asset_io::RemoteRefEmbedType, 1612 ) -> Result<()> { 1613 match embed_ref { 1614 crate::asset_io::RemoteRefEmbedType::Xmp(manifest_uri) => { 1615 let output_buf = Vec::new(); 1616 let mut output_stream = Cursor::new(output_buf); 1617 1618 // block so that source file is closed after embed 1619 { 1620 let mut source_stream = std::fs::File::open(asset_path)?; 1621 self.embed_reference_to_stream( 1622 &mut source_stream, 1623 &mut output_stream, 1624 RemoteRefEmbedType::Xmp(manifest_uri), 1625 )?; 1626 } 1627 1628 // write will replace exisiting contents 1629 output_stream.rewind()?; 1630 std::fs::write(asset_path, output_stream.into_inner())?; 1631 Ok(()) 1632 } 1633 crate::asset_io::RemoteRefEmbedType::StegoS(_) => Err(Error::UnsupportedType), 1634 crate::asset_io::RemoteRefEmbedType::StegoB(_) => Err(Error::UnsupportedType), 1635 crate::asset_io::RemoteRefEmbedType::Watermark(_) => Err(Error::UnsupportedType), 1636 } 1637 } 1638 1639 fn embed_reference_to_stream( 1640 &self, 1641 source_stream: &mut dyn CAIRead, 1642 output_stream: &mut dyn CAIReadWrite, 1643 embed_ref: RemoteRefEmbedType, 1644 ) -> Result<()> { 1645 match embed_ref { 1646 crate::asset_io::RemoteRefEmbedType::Xmp(manifest_uri) => { 1647 let xmp = match self.get_reader().read_xmp(source_stream) { 1648 Some(xmp) => add_provenance(&xmp, &manifest_uri)?, 1649 None => { 1650 let xmp = format!("http://ns.adobe.com/xap/1.0/\0 {}", MIN_XMP); 1651 add_provenance(&xmp, &manifest_uri)? 1652 } 1653 }; 1654 1655 let l = u64::value_from(xmp.len()) 1656 .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?; 1657 1658 let entry = IfdClonedEntry { 1659 entry_tag: XMP_TAG, 1660 entry_type: IFDEntryType::Byte as u16, 1661 value_count: l, 1662 value_bytes: xmp.as_bytes().to_vec(), 1663 }; 1664 tiff_clone_with_tags(output_stream, source_stream, vec![entry]) 1665 } 1666 crate::asset_io::RemoteRefEmbedType::StegoS(_) => Err(Error::UnsupportedType), 1667 crate::asset_io::RemoteRefEmbedType::StegoB(_) => Err(Error::UnsupportedType), 1668 crate::asset_io::RemoteRefEmbedType::Watermark(_) => Err(Error::UnsupportedType), 1669 } 1670 } 1671 } 1672 1673 impl ComposedManifestRef for TiffIO { 1674 // Return entire CAI block as Vec<u8> 1675 fn compose_manifest(&self, manifest_data: &[u8], _format: &str) -> Result<Vec<u8>> { 1676 Ok(manifest_data.to_vec()) 1677 } 1678 } 1679 1680 #[cfg(test)] 1681 pub mod tests { 1682 #![allow(clippy::panic)] 1683 #![allow(clippy::unwrap_used)] 1684 1685 use core::panic; 1686 1687 use tempfile::tempdir; 1688 1689 use super::*; 1690 use crate::utils::test::temp_dir_path; 1691 1692 #[test] 1693 fn test_read_write_manifest() { 1694 let data = "some data"; 1695 1696 let source = crate::utils::test::fixture_path("TUSCANY.TIF"); 1697 1698 let temp_dir = tempdir().unwrap(); 1699 let output = temp_dir_path(&temp_dir, "test.tif"); 1700 1701 std::fs::copy(source, &output).unwrap(); 1702 1703 let tiff_io = TiffIO {}; 1704 1705 // save data to tiff 1706 tiff_io.save_cai_store(&output, data.as_bytes()).unwrap(); 1707 1708 // read data back 1709 let loaded = tiff_io.read_cai_store(&output).unwrap(); 1710 1711 assert_eq!(&loaded, data.as_bytes()); 1712 } 1713 1714 #[test] 1715 fn test_write_xmp() { 1716 let data = "some data"; 1717 1718 let source = crate::utils::test::fixture_path("TUSCANY.TIF"); 1719 1720 let temp_dir = tempdir().unwrap(); 1721 let output = temp_dir_path(&temp_dir, "test.tif"); 1722 1723 std::fs::copy(source, &output).unwrap(); 1724 1725 let tiff_io = TiffIO {}; 1726 1727 // save data to tiff 1728 let eh = tiff_io.remote_ref_writer_ref().unwrap(); 1729 eh.embed_reference(&output, RemoteRefEmbedType::Xmp(data.to_string())) 1730 .unwrap(); 1731 1732 // read data back 1733 let mut output_stream = std::fs::File::open(&output).unwrap(); 1734 let xmp = tiff_io.read_xmp(&mut output_stream).unwrap(); 1735 let loaded = crate::utils::xmp_inmemory_utils::extract_provenance(&xmp).unwrap(); 1736 1737 assert_eq!(&loaded, data); 1738 } 1739 1740 #[test] 1741 fn test_remove_manifest() { 1742 let data = "some data"; 1743 1744 let source = crate::utils::test::fixture_path("TUSCANY.TIF"); 1745 1746 let temp_dir = tempdir().unwrap(); 1747 let output = temp_dir_path(&temp_dir, "test.tif"); 1748 1749 std::fs::copy(source, &output).unwrap(); 1750 1751 let tiff_io = TiffIO {}; 1752 1753 // first make sure that calling this without a manifest does not error 1754 tiff_io.remove_cai_store(&output).unwrap(); 1755 1756 // save data to tiff 1757 tiff_io.save_cai_store(&output, data.as_bytes()).unwrap(); 1758 1759 // read data back 1760 let loaded = tiff_io.read_cai_store(&output).unwrap(); 1761 1762 assert_eq!(&loaded, data.as_bytes()); 1763 1764 tiff_io.remove_cai_store(&output).unwrap(); 1765 1766 match tiff_io.read_cai_store(&output) { 1767 Err(Error::JumbfNotFound) => (), 1768 _ => panic!("should be no C2PA store"), 1769 } 1770 } 1771 1772 #[test] 1773 fn test_get_object_location() { 1774 let data = "some data"; 1775 1776 let source = crate::utils::test::fixture_path("TUSCANY.TIF"); 1777 1778 let temp_dir = tempdir().unwrap(); 1779 let output = temp_dir_path(&temp_dir, "test.tif"); 1780 1781 std::fs::copy(source, &output).unwrap(); 1782 1783 let tiff_io = TiffIO {}; 1784 1785 // save data to tiff 1786 tiff_io.save_cai_store(&output, data.as_bytes()).unwrap(); 1787 1788 // read data back 1789 let loaded = tiff_io.read_cai_store(&output).unwrap(); 1790 1791 assert_eq!(&loaded, data.as_bytes()); 1792 1793 let mut success = false; 1794 if let Ok(locations) = tiff_io.get_object_locations(&output) { 1795 for op in locations { 1796 if op.htype == HashBlockObjectType::Cai { 1797 let mut of = std::fs::File::open(&output).unwrap(); 1798 1799 let mut manifests_buf: Vec<u8> = vec![0u8; op.length]; 1800 of.seek(SeekFrom::Start(op.offset as u64)).unwrap(); 1801 of.read_exact(manifests_buf.as_mut_slice()).unwrap(); 1802 if crate::hash_utils::vec_compare(&manifests_buf, data.as_bytes()) { 1803 success = true; 1804 } 1805 } 1806 } 1807 } 1808 assert!(success); 1809 } 1810 /* disable until I find smaller DNG 1811 #[test] 1812 fn test_read_write_dng_manifest() { 1813 let data = "some data"; 1814 1815 let source = crate::utils::test::fixture_path("test.DNG"); 1816 //let source = crate::utils::test::fixture_path("sample1.dng"); 1817 1818 let temp_dir = tempdir().unwrap(); 1819 let output = temp_dir_path(&temp_dir, "test.DNG"); 1820 1821 std::fs::copy(&source, &output).unwrap(); 1822 1823 let tiff_io = TiffIO {}; 1824 1825 // save data to tiff 1826 tiff_io.save_cai_store(&output, data.as_bytes()).unwrap(); 1827 1828 // read data back 1829 println!("Reading TIFF"); 1830 let loaded = tiff_io.read_cai_store(&output).unwrap(); 1831 1832 assert_eq!(&loaded, data.as_bytes()); 1833 } 1834 #[test] 1835 fn test_read_write_dng_parse() { 1836 //let data = "some data"; 1837 1838 let source = crate::utils::test::fixture_path("test.DNG"); 1839 let mut f = std::fs::File::open(&source).unwrap(); 1840 1841 let (idfs, token, _endianness, _big_tiff) = map_tiff(&mut f).unwrap(); 1842 1843 println!("IFD {}", idfs[token].data.entry_cnt); 1844 } 1845 */ 1846 }