c2pa-rs

A fork of https://github.com/contentauth/c2pa-rs/
git clone git://archive.git.mtrnord.blog/mtrnords-photography-manager/c2pa-rs.git
Log | Files | Refs | README

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 }