tiff_encoder/ifd/
values.rs

1//! The values contained or pointed at by an IFD Field.
2//!
3//! There are three groups of [`FieldValues`]: [`TiffTypeValues`],
4//! [`Offsets`] and [`OffsetsToIfds`]. The first represents a list
5//! of values of any given [`TiffType`]. The second represents a
6//! list of [`LONG`] values, each pointing to a specific [`Datablock`].
7//! The third represents a list of [`IFD`] values, each pointing to
8//! an [`Ifd`].
9//!
10//! [`FieldValues`]: trait.FieldValues.html
11//! [`TiffTypeValues`]: struct.TiffTypeValues.html
12//! [`Offsets`]: struct.Offsets.html
13//! [`OffsetsToIfds`]: struct.OffsetsToIfds.html
14//! [`TiffType`]: ../types/trait.TiffType.html
15//! [`LONG`]: ../types/struct.LONG.html
16//! [`IFD`]: ../types/struct.IFD.html
17//! [`Datablock`]: ../../write/trait.Datablock.html
18
19use std::io;
20
21use crate::ifd::types::{TiffType, IFD, LONG};
22use crate::ifd::{AllocatedIfdChain, IfdChain};
23use crate::write::{Cursor, Datablock, EndianFile};
24
25/// The values contained or pointed at by an IFD Field.
26///
27/// There are three groups of `FieldValues`: [`TiffTypeValues`],
28/// [`Offsets`] and [`OffsetsToIfds`]. The first represents a list
29/// of values of any given [`TiffType`]. The second represents a
30/// list of [`LONG`] values, each pointing to a specific [`Datablock`].
31/// The third represents a list of [`IFD`] values, each pointing to
32/// an [`Ifd`].
33///
34/// This trait is sealed. It is not possible to implement it outside
35/// this crate.
36///
37/// [`TiffTypeValues`]: struct.TiffTypeValues.html
38/// [`Offsets`]: struct.Offsets.html
39/// [`OffsetsToIfds`]: struct.OffsetsToIfds.html
40/// [`TiffType`]: ../types/trait.TiffType.html
41/// [`LONG`]: ../types/struct.LONG.html
42/// [`IFD`]: ../types/struct.IFD.html
43/// [`Datablock`]: ../../write/trait.Datablock.html
44pub trait FieldValues: private::Sealed {
45    /// The number of values the field contains.
46    #[doc(hidden)]
47    fn count(&self) -> u32;
48    /// The sum of the size of every value in this field.
49    ///
50    /// This doesn't include `Datablocks` owned by this field.
51    #[doc(hidden)]
52    fn size(&self) -> u32;
53    /// Allocates the needed space in the given `Cursor`, transforming into
54    /// an `AllocatedFieldValues`.
55    #[doc(hidden)]
56    fn allocate(self: Box<Self>, c: &mut Cursor) -> Box<AllocatedFieldValues>;
57}
58
59/// Allocated form of `FieldValues`
60#[doc(hidden)]
61pub trait AllocatedFieldValues {
62    /// The number of values the field contains.
63    fn count(&self) -> u32;
64    /// The sum of the size of every value in this field.
65    ///
66    /// This doesn't include `Datablocks` owned by this field.
67    fn size(&self) -> u32;
68    /// The offset to the first value (counting from the beginning of the file)
69    /// if the values don't fit in the IFD entry (in other words, if `size()` is
70    /// bigger than 4 bytes).
71    fn position(&self) -> Option<u32>;
72    /// The TIFF 16-bit code that identifies the type of the values of the field.
73    fn type_id(&self) -> u16;
74    /// Write the values to the given `EndianFile`, as well as any other data
75    /// they point to.
76    fn write_to(self: Box<Self>, file: &mut EndianFile) -> io::Result<()>;
77}
78
79/// Seals FieldValues, so that it can only be implemented inside
80/// the crate. There are only three types of FieldValues:
81/// `Offsets` to datablocks, `OffsetsToIfds` and `TiffTypeValues`.
82mod private {
83    pub trait Sealed {}
84    impl<T: super::Datablock> Sealed for super::Offsets<T> {}
85    impl<T: super::TiffType> Sealed for super::TiffTypeValues<T> {}
86    impl Sealed for super::OffsetsToIfds {}
87}
88
89/// A list of [`LONG`] values, each pointing to a specific
90/// [`Datablock`].
91///
92/// This structure owns the list of Datablocks instead, so the user
93/// doesn't have to deal with the offsets in the file. It is responsible
94/// for writing both the offsets and the blocks of data.
95///
96/// [`LONG`]: ../types/struct.LONG.html
97/// [`Datablock`]: ../../write/trait.Datablock.html
98pub struct Offsets<T: Datablock> {
99    pub data: Vec<T>,
100}
101impl<T: Datablock + 'static> Offsets<T> {
102    /// Creates a new `Offsets` instance from a vector of [`Datablock`]s.
103    ///
104    /// [`Datablock`]: ../../write/trait.Datablock.html
105    pub fn new(datablocks: Vec<T>) -> Self {
106        Offsets { data: datablocks }
107    }
108
109    /// Creates a new `Offsets` instance from a single [`Datablock`].
110    ///
111    /// [`Datablock`]: ../../write/trait.Datablock.html
112    pub fn single(datablock: T) -> Self {
113        Offsets::new(vec![datablock])
114    }
115}
116impl<T: Datablock + 'static> FieldValues for Offsets<T> {
117    #[doc(hidden)]
118    fn count(&self) -> u32 {
119        self.data.len() as u32
120    }
121
122    #[doc(hidden)]
123    fn size(&self) -> u32 {
124        LONG::size() * self.count()
125    }
126
127    #[doc(hidden)]
128    fn allocate(self: Box<Self>, c: &mut Cursor) -> Box<AllocatedFieldValues> {
129        let position = Some(c.allocated_bytes());
130        if self.data.len() == 1 {
131            // If there is just one block, the position will point directly at it.
132            // As such, the offsets vector will be kept empty.
133            let offsets = Vec::new();
134            let block_size = self.data.get(0).unwrap().size(); // Data has size of 1
135
136            // Internally allocate an extra byte if size is odd.
137            // This guarantes that the next element will
138            // begin on a word-boundary.
139            c.allocate(if block_size % 2 == 0 {
140                block_size
141            } else {
142                block_size + 1
143            });
144
145            Box::new(AllocatedOffsets {
146                position,
147                offsets,
148                data: self.data,
149            })
150        } else {
151            c.allocate(self.size());
152            let mut offsets = Vec::with_capacity(self.data.len());
153
154            for block in self.data.iter() {
155                offsets.push(LONG(c.allocated_bytes()));
156                c.allocate(if block.size() % 2 == 0 {
157                    block.size()
158                } else {
159                    block.size() + 1
160                });
161            }
162
163            Box::new(AllocatedOffsets {
164                position,
165                offsets,
166                data: self.data,
167            })
168        }
169    }
170}
171
172/// Allocated form of `Offsets`
173struct AllocatedOffsets<T: Datablock> {
174    position: Option<u32>,
175    offsets: Vec<LONG>,
176    data: Vec<T>,
177}
178impl<T: Datablock> AllocatedFieldValues for AllocatedOffsets<T> {
179    fn count(&self) -> u32 {
180        self.data.len() as u32
181    }
182
183    fn size(&self) -> u32 {
184        LONG::size() * self.count()
185    }
186
187    fn position(&self) -> Option<u32> {
188        self.position
189    }
190
191    fn type_id(&self) -> u16 {
192        LONG::id()
193    }
194
195    fn write_to(self: Box<Self>, file: &mut EndianFile) -> io::Result<()> {
196        let unboxed = *self;
197        let Self { data, offsets, .. } = unboxed;
198        for offset in offsets {
199            offset.write_to(file)?;
200        }
201        for block in data {
202            let file_initial = file.written_bytes();
203            let block_size = block.size();
204            block.write_to(file)?;
205            let written_size = file.written_bytes() - file_initial;
206            // Internally write an extra byte if size is odd.
207            // This guarantes that the next element will
208            // begin on a word-boundary.
209            if written_size % 2 == 1 {
210                file.write_arbitrary_byte()?
211            }
212            if written_size != block_size {
213                panic!(
214                    "The number of bytes allocated by the Datablock ({}) is different from the number of bytes written to the file ({}).", 
215                    block_size, written_size
216                )
217            }
218        }
219
220        Ok(())
221    }
222}
223
224/// A list of values of any given [`TiffType`].
225///
226/// [`TiffType`]: ../types/trait.TiffType.html
227#[derive(Debug, PartialEq)]
228pub struct TiffTypeValues<T: TiffType> {
229    values: Vec<T>,
230}
231impl<T: TiffType + 'static> TiffTypeValues<T> {
232    /// Creates a new instance of `TiffTypeValues` from a vector
233    /// of instances of any given [`TiffType`].
234    ///
235    /// [`TiffType`]: ../types/trait.TiffType.html
236    pub fn new(values: Vec<T>) -> Self {
237        if values.len() == 0 {
238            panic!("Cannot create an empty instance of TiffTypeValues")
239        }
240        TiffTypeValues { values }
241    }
242}
243impl<T: TiffType + 'static> FieldValues for TiffTypeValues<T> {
244    #[doc(hidden)]
245    fn count(&self) -> u32 {
246        self.values.len() as u32
247    }
248
249    #[doc(hidden)]
250    fn size(&self) -> u32 {
251        T::size() * self.count()
252    }
253
254    #[doc(hidden)]
255    fn allocate(self: Box<Self>, c: &mut Cursor) -> Box<AllocatedFieldValues> {
256        let position = if self.size() <= 4 {
257            None
258        } else {
259            // If the entry size is odd, it will need to allocate an extra byte
260            // so that offsets continue to respect the word boundary
261            let size = self.size() + self.size() % 2;
262            let pos = c.allocated_bytes();
263            c.allocate(size);
264            Some(pos)
265        };
266
267        Box::new(AllocatedTiffTypeValues {
268            position,
269            values: self.values,
270        })
271    }
272}
273
274/// Allocated form of `TiffTypeValues`
275struct AllocatedTiffTypeValues<T: TiffType> {
276    position: Option<u32>,
277    values: Vec<T>,
278}
279impl<T: TiffType> AllocatedFieldValues for AllocatedTiffTypeValues<T> {
280    fn count(&self) -> u32 {
281        self.values.len() as u32
282    }
283
284    fn size(&self) -> u32 {
285        T::size() * self.count()
286    }
287
288    fn position(&self) -> Option<u32> {
289        self.position
290    }
291
292    fn type_id(&self) -> u16 {
293        T::id()
294    }
295
296    fn write_to(self: Box<Self>, file: &mut EndianFile) -> io::Result<()> {
297        let size = self.size();
298        for value in self.values {
299            let file_initial = file.written_bytes();
300            value.write_to(file)?;
301            let written_size = file.written_bytes() - file_initial;
302            if written_size != T::size() {
303                panic!(
304                    "The size indicated ({}) is different from the number of bytes the type has written to the file ({}).", 
305                    T::size(), written_size
306                )
307            }
308        }
309
310        if size % 2 == 1 && size > 4 {
311            file.write_arbitrary_byte()?;
312        }
313        Ok(())
314    }
315}
316
317/// A list of [`IFD`] values, each pointing to a specific
318/// [`Ifd`].
319///
320/// This structure owns a list of [`IfdChain`]s instead, so the user
321/// doesn't have to deal with the offsets in the file. Each [`IFD`]
322/// value will point to the first element of each [`IfdChain`]. Each
323/// of those `Ifd`s will point to the next one in their chain (if they
324/// are not the last of their chain) and so on.
325///
326/// It is responsible for writing both the offsets and all the [`Ifd`]s.
327///
328/// [`LONG`]: ../types/struct.LONG.html
329/// [`IFD`]: ../types/struct.IFD.html
330/// [`Ifd`]: ../struct.Ifd.html
331/// [`IfdChain`]: ../struct.IfdChain.html
332pub struct OffsetsToIfds {
333    pub data: Vec<IfdChain>,
334}
335impl OffsetsToIfds {
336    /// Creates a new `OffsetsToIfds` instance from a vector of [`IfdChain`]s.
337    ///
338    /// [`IfdChain`]: ../struct.IfdChain.html
339    pub fn new(ifds: Vec<IfdChain>) -> Self {
340        OffsetsToIfds { data: ifds }
341    }
342}
343impl FieldValues for OffsetsToIfds {
344    #[doc(hidden)]
345    fn count(&self) -> u32 {
346        self.data.len() as u32
347    }
348
349    #[doc(hidden)]
350    fn size(&self) -> u32 {
351        IFD::size() * self.count()
352    }
353
354    #[doc(hidden)]
355    fn allocate(self: Box<Self>, c: &mut Cursor) -> Box<AllocatedFieldValues> {
356        let position = Some(c.allocated_bytes());
357        if self.data.len() == 1 {
358            // If there is just one block, the position will point directly at it.
359            // As such, the offsets vector will be kept empty.
360            let offsets = Vec::new();
361            let ifd = self.data.into_iter().next().unwrap(); // Data has size of 1
362            let allocated_data = vec![ifd.allocate(c)];
363
364            Box::new(AllocatedOffsetsToIfds {
365                position,
366                offsets,
367                data: allocated_data,
368            })
369        } else {
370            c.allocate(self.size());
371            let mut offsets = Vec::with_capacity(self.data.len());
372            let mut allocated_data = Vec::with_capacity(self.data.len());
373
374            for ifd in self.data {
375                offsets.push(IFD(c.allocated_bytes()));
376                allocated_data.push(ifd.allocate(c));
377            }
378
379            Box::new(AllocatedOffsetsToIfds {
380                position,
381                offsets,
382                data: allocated_data,
383            })
384        }
385    }
386}
387
388/// Allocated form of `OffsetsToIfds`
389struct AllocatedOffsetsToIfds {
390    position: Option<u32>,
391    offsets: Vec<IFD>,
392    data: Vec<AllocatedIfdChain>,
393}
394impl AllocatedFieldValues for AllocatedOffsetsToIfds {
395    fn count(&self) -> u32 {
396        self.data.len() as u32
397    }
398
399    fn size(&self) -> u32 {
400        IFD::size() * self.count()
401    }
402
403    fn position(&self) -> Option<u32> {
404        self.position
405    }
406
407    fn type_id(&self) -> u16 {
408        IFD::id()
409    }
410
411    fn write_to(self: Box<Self>, file: &mut EndianFile) -> io::Result<()> {
412        let unboxed = *self;
413        let Self { data, offsets, .. } = unboxed;
414        for offset in offsets {
415            offset.write_to(file)?;
416        }
417        for ifd in data.into_iter() {
418            ifd.write_to(file)?;
419        }
420
421        Ok(())
422    }
423}