tiff_encoder/ifd/
ifd.rs

1use std::collections::BTreeMap;
2use std::io;
3
4use crate::ifd::tags::{self, FieldTag};
5use crate::ifd::values::{AllocatedFieldValues, FieldValues, OffsetsToIfds};
6use crate::write::{Cursor, EndianFile};
7
8/// An ordered list of [`Ifd`]s, each pointing to the next one.
9///
10/// The last `Ifd` doesn't point to any other.
11///
12/// Because any IFD could technically point to a next one, in most
13/// functions that one would expect to input an `Ifd`, its parameters
14/// actually ask for an `IfdChain`.
15///
16/// [`Ifd`]: struct.Ifd.html
17pub struct IfdChain(Vec<Ifd>);
18impl IfdChain {
19    /// Creates a new `IfdChain` from a vector of [`Ifd`]s.
20    ///  
21    /// # Panics
22    ///
23    /// The TIFF specification requires that each IFD must have at least one entry.
24    ///
25    /// Trying to create an `IfdChain` with one or more empty `Ifd`s will `panic`.
26    ///
27    /// [`Ifd`]: struct.Ifd.html
28    pub fn new(ifds: Vec<Ifd>) -> IfdChain {
29        if ifds.len() == 0 {
30            panic!("Cannot create a chain without IFDs.")
31        }
32        for ifd in ifds.iter() {
33            if ifd.entry_count() == 0 {
34                panic!("Tried to create a chain containing empty IFDs.\nEach IFD must have at least 1 entry.")
35            }
36        }
37        IfdChain(ifds)
38    }
39
40    /// Creates a new `IfdChain` from a single [`Ifd`].
41    ///
42    /// # Panics
43    ///
44    /// The TIFF specification requires that each IFD must have at least one entry.
45    ///
46    /// Trying to create an `IfdChain` from an empty `Ifd` will `panic`.
47    ///
48    ///
49    /// [`Ifd`]: struct.Ifd.html
50    pub fn single(ifd: Ifd) -> IfdChain {
51        IfdChain::new(vec![ifd])
52    }
53
54    /// Allocates every `Ifd` in the chain, moving the given `Cursor` forwards.
55    ///
56    /// Calling this will transform `self` into an `AllocatedIfdChain`.
57    pub(crate) fn allocate(self, c: &mut Cursor) -> AllocatedIfdChain {
58        let len = self.0.len();
59        let mut ifds = Vec::with_capacity(len);
60        for (index, ifd) in self.0.into_iter().enumerate() {
61            ifds.push(ifd.allocate(c, index + 1 == len));
62        }
63        AllocatedIfdChain(ifds)
64    }
65}
66
67/// An `IfdChain` that called `allocate(&mut Cursor)` and is
68/// ready to write to a file.
69pub(crate) struct AllocatedIfdChain(Vec<AllocatedIfd>);
70impl AllocatedIfdChain {
71    /// Write all of the `IFD`s in this chain to the given `EndianFile`.
72    pub(crate) fn write_to(self, file: &mut EndianFile) -> io::Result<()> {
73        for ifd in self.0.into_iter() {
74            ifd.write_to(file)?;
75        }
76        Ok(())
77    }
78}
79
80/// A structure that holds both an IFD and all the values pointed at
81/// by its entries.
82///
83/// An image file directory (IFD) contains information about the image, as
84/// well as pointers to the actual image data (both stored as entries).
85///
86/// In a TIFF file, an IFD may point to another IFD with its last 4
87/// bytes. To abstract the user of this crate from the position of each
88/// structure in the file, this link between `Ifd`s is represented by
89/// an [`IfdChain`]. Because any IFD could technically point to a next
90/// one, in most functions that one would expect to input an `Ifd`, its
91/// parameters actually ask for an `IfdChain`.
92///
93/// One can easily create an `IfdChain` of a single `Ifd` calling the
94/// method [`single()`] on that `Ifd`.
95///
96/// [`IfdChain`]: struct.IfdChain.html
97/// [`single()`]: #method.single
98pub struct Ifd {
99    entries: BTreeMap<FieldTag, Box<FieldValues>>,
100}
101impl Ifd {
102    /// Creates a new empty `Ifd`.
103    ///  
104    /// Note that an empty IFD is prohibited by the TIFF specification.
105    /// As such, it is not possible to directly use the resulting `Ifd`
106    /// alone in the creation of a TIFF file.
107    ///
108    /// However, one can chain this function with methods such as
109    /// [`with_entry(FieldTag, FieldValues)`] in order to build a valid `Ifd`.
110    ///
111    /// [`with_entry(FieldTag, FieldValues)`]: #method.with_entry
112    pub fn new() -> Ifd {
113        Ifd {
114            entries: BTreeMap::new(),
115        }
116    }
117
118    /// Returns the same `Ifd`, but adding the given pair of Tag and Values.
119    ///
120    /// Because it returns `Self`, it is possible to chain this method.
121    ///
122    /// # Examples
123    ///
124    /// Creating a [`TiffFile`] with some arbitrary entries.
125    ///
126    /// Note that the order in which entries are added is irrelevant. Internally,
127    /// the `Ifd` will automatically arrange them by ascending order of tags, as
128    /// specified by the TIFF specification.
129    ///
130    /// ```
131    /// #[macro_use]
132    /// extern crate tiff_encoder;
133    /// use tiff_encoder::prelude::*;
134    ///
135    /// # fn main() {
136    /// let ifd = Ifd::new()
137    ///     .with_entry(0x0000, BYTE![0])
138    ///     .with_entry(0x00FF, LONG![500])
139    ///     .with_entry(0xA01F, SHORT![50, 2, 0, 3])
140    ///     .with_entry(0x0005, ASCII!["Hello TIFF!"])
141    ///     .with_entry(0x0100, UNDEFINED![0x42, 0x42, 0x42, 0x42]);
142    /// # }
143    /// ```
144    ///
145    /// # Panics
146    ///
147    /// In order to protect the user of this crate, trying to add a value
148    /// to an already existing entry with this method is considered a mistake
149    /// and will `panic`.
150    ///
151    /// Other functions that insert members to the `Ifd` will have an "Entries"
152    /// section, where they'll specify which entries are inserted.
153    ///
154    /// [`TiffFile`]: ../struct.TiffFile.html
155    pub fn with_entry<T: FieldValues + 'static>(mut self, tag: FieldTag, value: T) -> Self {
156        if self.entries.insert(tag, Box::new(value)).is_some() {
157            panic!("Tried to add the same tag twice.");
158        }
159        self
160    }
161
162    /// Returns the same `Ifd`, after adding the specified pairs of Tags and Values.
163    ///
164    /// Because it returns `Self`, it is possible to chain this method.
165    ///
166    /// # Panics
167    ///
168    /// If the inserted entries already exist, this function will `panic`.
169    ///
170    pub fn with_entries<C: IntoIterator<Item=(FieldTag, Box<FieldValues>)>>(mut self, entries: C) -> Self {
171        entries.into_iter().for_each(|(tag, value)| {
172            if self.entries.insert(tag, value).is_some() {
173                panic!("Tried to add the same tag twice.");
174            }
175        });
176
177        self
178    }
179
180    /// Returns the same `Ifd`, but adding the given subifds.
181    ///
182    /// Because it returns `Self`, it is possible to chain this method.
183    ///
184    /// # Entries
185    ///
186    /// Using this method will automatically insert the entry 0x014A (tag::SubIFDs).
187    ///
188    /// # Panics
189    ///
190    /// If the inserted entries already exist, this function will `panic`.
191    ///
192    /// [`TiffFile`]: ../struct.TiffFile.html
193    pub fn with_subifds(self, subifds: Vec<IfdChain>) -> Self {
194        self.with_entry(tags::SubIFDs, OffsetsToIfds::new(subifds))
195    }
196
197    /// Returns an [`IfdChain`] containing solely this `Ifd`.
198    ///
199    /// In other words, it marks this `Ifd` as the single element
200    /// of its chain.
201    ///
202    /// [`IfdChain`]: struct.IfdChain.html
203    pub fn single(self) -> IfdChain {
204        IfdChain::single(self)
205    }
206
207    /// Returns the number of entries present in this `Ifd`.
208    fn entry_count(&self) -> u32 {
209        self.entries.len() as u32
210    }
211
212    /// Returns the number of bytes occupied by this `Ifd` in its binary form.
213    ///
214    /// Note that this only includes the IFD itself, not the values associated
215    /// with it that don't fit in their entry nor the blocks of data pointed at by
216    /// some of the fields.
217    fn size(&self) -> u32 {
218        self.entry_count() * 12 + 6
219    }
220
221    /// Allocates space in the given `Cursor` for this `Ifd`, as well as
222    /// the field values associated with it that don't fit in their entry.
223    ///
224    /// Becomes aware of the position of the next IFD in its chain (if
225    /// its not the last IFD), thus transforming into an `AllocatedIFd`.
226    fn allocate(self, c: &mut Cursor, last_ifd: bool) -> AllocatedIfd {
227        c.allocate(self.size());
228
229        let mut entries = BTreeMap::new();
230        for (tag, value) in self.entries {
231            entries.insert(tag, value.allocate(c));
232        }
233
234        let offset_to_next_ifd = if last_ifd {
235            None
236        } else {
237            Some(c.allocated_bytes())
238        };
239
240        AllocatedIfd {
241            entries,
242            offset_to_next_ifd,
243        }
244    }
245}
246
247/// Representation of a `Ifd` that called `allocate(&mut Cursor, bool)` and is
248/// ready to write to a file.
249struct AllocatedIfd {
250    entries: BTreeMap<FieldTag, Box<AllocatedFieldValues>>,
251    offset_to_next_ifd: Option<u32>,
252}
253
254impl AllocatedIfd {
255    /// Write this IFD to the given `EndianFile`, as well as any values
256    /// associated with its entries.
257    fn write_to(self, file: &mut EndianFile) -> io::Result<()> {
258        let mut big_values = Vec::new();
259
260        file.write_u16(self.entries.len() as u16)?;
261
262        for (tag, value) in self.entries.into_iter() {
263            let value = Self::write_entry_to((tag, value), file)?;
264            if let Some(value) = value {
265                big_values.push(value);
266            }
267        }
268        file.write_u32(self.offset_to_next_ifd.unwrap_or(0))?;
269
270        for value in big_values {
271            value.write_to(file)?;
272        }
273
274        Ok(())
275    }
276
277    /// Write a single entry of the IFD. If its value doesn't fit,
278    /// returns that value back so it can be written later, after
279    /// the IFD.
280    fn write_entry_to(
281        (tag, value): (FieldTag, Box<AllocatedFieldValues>),
282        file: &mut EndianFile,
283    ) -> io::Result<Option<Box<AllocatedFieldValues>>> {
284        file.write_u16(tag)?;
285        file.write_u16(value.type_id())?;
286        file.write_u32(value.count())?;
287
288        match value.position() {
289            Some(position) => {
290                file.write_u32(position)?;
291                Ok(Some(value))
292            }
293            None => {
294                let size = value.size();
295                value.write_to(file)?;
296                for _ in 0..(4 - size) {
297                    file.write_u8(0)?;
298                }
299                Ok(None)
300            }
301        }
302    }
303}