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}