tiff_encoder/
write.rs

1//! Helpers to write the file.
2
3use std::fs;
4use std::io;
5use std::io::Write;
6
7use byteorder::{BigEndian, LittleEndian, WriteBytesExt};
8
9use crate::ifd::values::Offsets;
10
11/// The byte order used within the TIFF file.
12///
13/// There are two possible values: II (little-endian or Intel format)
14/// and MM (big-endian or Motorola format).
15#[derive(Clone, Copy)]
16pub enum Endianness {
17    /// Intel byte order, also known as little-endian.
18    ///
19    /// The byte order is always from the least significant byte to
20    /// the most significant byte.
21    II,
22
23    /// Motorola byte order, also known as big-endian.
24    ///
25    /// The byte order is always from the most significant byte to
26    /// the least significant byte.
27    MM,
28}
29
30impl Endianness {
31    /// Returns the u16 value that represents the given endianness
32    /// in a Tagged Image File Header.
33    pub(crate) fn id(&self) -> u16 {
34        match &self {
35            Endianness::II => 0x4949,
36            Endianness::MM => 0x4d4d,
37        }
38    }
39}
40
41/// Used during the allocation phase of the process of creating
42/// a TIFF file.
43///
44/// Holds the number of bytes that were allocated, in order to
45/// calculate the needed offsets.
46#[doc(hidden)]
47pub struct Cursor(u32);
48impl Cursor {
49    /// Creates a new `Cursor` with no bytes allocated.
50    pub(crate) fn new() -> Self {
51        Cursor(0)
52    }
53
54    /// Allocates a number of bytes to the `Cursor`.
55    ///
56    /// # Panics
57    ///
58    /// The maximum size of a TIFF file is 2**32 bits. Attempting
59    /// to allocate more space than that will `panic`.
60    pub(crate) fn allocate(&mut self, n: u32) {
61        self.0 = match self.0.checked_add(n) {
62            Some(val) => val,
63            None => panic!("Attempted to write a TIFF file bigger than 2**32 bytes."),
64        };
65    }
66
67    /// Returns the number of already allocated bytes.
68    pub(crate) fn allocated_bytes(&self) -> u32 {
69        self.0
70    }
71}
72
73/// Helper structure that provides convenience methods to write to
74/// a `fs::File`, being aware of the file's [`Endianness`].
75///
76/// [`Endianness`]: enum.Endianness.html
77pub struct EndianFile {
78    file: fs::File,
79    byte_order: Endianness,
80    written_bytes: u32,
81}
82
83impl Into<fs::File> for EndianFile {
84    fn into(self) -> fs::File {
85        self.file
86    }
87}
88
89impl EndianFile {
90    /// Gets the number of written bytes to this file.
91    pub(crate) fn new(file: fs::File, byte_order: Endianness) -> Self {
92        Self {
93            file,
94            byte_order,
95            written_bytes: 0,
96        }
97    }
98
99    /// Gets the number of written bytes to this file.
100    pub(crate) fn written_bytes(&self) -> u32 {
101        self.written_bytes
102    }
103}
104
105impl EndianFile {
106    /// Writes a u8 to the file.
107    ///
108    /// # Errors
109    ///
110    /// This method returns the same errors as [`Write::write_all`].
111    ///
112    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
113    pub fn write_u8(&mut self, n: u8) -> io::Result<()> {
114        self.written_bytes += 1;
115        self.file.write_u8(n)
116    }
117
118    /// Writes a slice of bytes to a file.
119    ///
120    /// This is much more efficient than calling [`write_u8`] in a loop if you have list
121    /// of bytes to write.
122    ///
123    /// # Errors
124    ///
125    /// This method returns the same errors as [`Write::write_all`].
126    ///
127    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
128    pub fn write_all_u8(&mut self, bytes: &[u8]) -> io::Result<()> {
129        self.written_bytes += bytes.len() as u32;
130        self.file.write_all(bytes)
131    }
132
133    /// Writes a u16 to the file.
134    ///
135    /// # Errors
136    ///
137    /// This method returns the same errors as [`Write::write_all`].
138    ///
139    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
140    pub fn write_u16(&mut self, n: u16) -> io::Result<()> {
141        self.written_bytes += 2;
142        match self.byte_order {
143            Endianness::II => {
144                self.file.write_u16::<LittleEndian>(n)?;
145            }
146            Endianness::MM => {
147                self.file.write_u16::<BigEndian>(n)?;
148            }
149        }
150        Ok(())
151    }
152
153    /// Writes a u32 to the file.
154    ///
155    /// # Errors
156    ///
157    /// This method returns the same errors as [`Write::write_all`].
158    ///
159    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
160    pub fn write_u32(&mut self, n: u32) -> io::Result<()> {
161        self.written_bytes += 4;
162        match self.byte_order {
163            Endianness::II => {
164                self.file.write_u32::<LittleEndian>(n)?;
165            }
166            Endianness::MM => {
167                self.file.write_u32::<BigEndian>(n)?;
168            }
169        }
170        Ok(())
171    }
172
173    /// Writes a i8 to the file.
174    ///
175    /// # Errors
176    ///
177    /// This method returns the same errors as [`Write::write_all`].
178    ///
179    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
180    pub fn write_i8(&mut self, n: i8) -> io::Result<()> {
181        self.written_bytes += 1;
182        self.file.write_i8(n)
183    }
184
185    /// Writes a i16 to the file.
186    ///
187    /// # Errors
188    ///
189    /// This method returns the same errors as [`Write::write_all`].
190    ///
191    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
192    pub fn write_i16(&mut self, n: i16) -> io::Result<()> {
193        self.written_bytes += 2;
194        match self.byte_order {
195            Endianness::II => {
196                self.file.write_i16::<LittleEndian>(n)?;
197            }
198            Endianness::MM => {
199                self.file.write_i16::<BigEndian>(n)?;
200            }
201        }
202        Ok(())
203    }
204
205    /// Writes a i32 to the file.
206    ///
207    /// # Errors
208    ///
209    /// This method returns the same errors as [`Write::write_all`].
210    ///
211    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
212    pub fn write_i32(&mut self, n: i32) -> io::Result<()> {
213        self.written_bytes += 4;
214        match self.byte_order {
215            Endianness::II => {
216                self.file.write_i32::<LittleEndian>(n)?;
217            }
218            Endianness::MM => {
219                self.file.write_i32::<BigEndian>(n)?;
220            }
221        }
222        Ok(())
223    }
224
225    /// Writes a f32 to the file.
226    ///
227    /// # Errors
228    ///
229    /// This method returns the same errors as [`Write::write_all`].
230    ///
231    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
232    pub fn write_f32(&mut self, n: f32) -> io::Result<()> {
233        self.written_bytes += 4;
234        match self.byte_order {
235            Endianness::II => {
236                self.file.write_f32::<LittleEndian>(n)?;
237            }
238            Endianness::MM => {
239                self.file.write_f32::<BigEndian>(n)?;
240            }
241        }
242        Ok(())
243    }
244
245    /// Writes a f64 to the file.
246    ///
247    /// # Errors
248    ///
249    /// This method returns the same errors as [`Write::write_all`].
250    ///
251    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
252    pub fn write_f64(&mut self, n: f64) -> io::Result<()> {
253        self.written_bytes += 8;
254        match self.byte_order {
255            Endianness::II => {
256                self.file.write_f64::<LittleEndian>(n)?;
257            }
258            Endianness::MM => {
259                self.file.write_f64::<BigEndian>(n)?;
260            }
261        }
262        Ok(())
263    }
264
265    /// Writes an arbitraty byte to the file.
266    ///
267    /// This is useful when there is need to write an extra byte
268    /// to guarantee that all offsets are even but that byte
269    /// doesn't really hold any information.
270    pub(crate) fn write_arbitrary_byte(&mut self) -> io::Result<()> {
271        self.written_bytes += 1;
272        self.file.write_u8(0)
273    }
274}
275
276/// A block of data in the file pointed to by a field value, but
277/// that isn't part of the field itself (such as image strips).
278///
279/// It is also possible to store any block of data in a [`ByteBlock`],
280/// but that would require to know the [`Endianness`] of the file
281/// beforehand, so the bytes are written in the correct order.
282///
283/// Using a `Datablock`, on the other hand, allows to make use
284/// of the functionality of an [`EndianFile`], so the data can be
285/// written without worrying about the endianness.
286///
287/// # Examples
288///
289/// Creating a DataBlock for `Vec<u32>`:
290/// ```
291/// use std::io;
292/// use tiff_encoder::write::{Datablock, EndianFile};
293/// use tiff_encoder::ifd::values::Offsets;
294///
295/// // Create a block that wraps the u32 data.
296/// struct U32Block(Vec<u32>);
297/// // Implement datablock functions
298/// impl Datablock for U32Block {
299///     fn size(&self) -> u32 {
300///         // Each u32 occupies 4 bytes.
301///         self.0.len() as u32 * 4
302///     }
303///     fn write_to(self, file: &mut EndianFile) -> io::Result<()> {
304///         for val in self.0 {
305///             file.write_u32(val)?
306///         }
307///         Ok(())
308///     }
309/// }
310/// // (Optional) implement some convenient functions to construct Offsets
311/// impl U32Block {
312///     // Construct an Offsets to multiple U32Block
313///     pub fn offsets(blocks: Vec<Vec<u32>>) -> Offsets<Self> {
314///         Offsets::new(blocks.into_iter().map(|block| U32Block(block)).collect())
315///     }
316///     // Construct an Offsets to a single U32Block
317///     pub fn single(block: Vec<u32>) -> Offsets<Self> {
318///         U32Block::offsets(vec![block])
319///     }
320/// }
321///
322/// // A vector holding arbitrary u32 data.
323/// // This is the data we want to store in the U32Block.
324/// let data_32bits: Vec<u32> = vec![0; 65536];
325///
326/// // This is the value that can be used directly as an IFD entry value.
327/// let byte_block = U32Block::single(data_32bits);
328/// ```
329///
330/// [`ByteBlock`]: struct.ByteBlock.html
331/// [`Endianness`]: enum.Endianness.html
332/// [`EndianFile`]: struct.EndianFile.html
333pub trait Datablock {
334    /// The number of bytes occupied by this `Datablock`.
335    ///
336    /// # Panics
337    ///
338    /// The number of written bytes to the [`EndianFile`] in
339    /// [`write_to(self, &mut EndianFile)`] must be the same value returned
340    /// by this function.
341    ///
342    /// Failing to meet that specification will `panic`.
343    ///
344    /// [`EndianFile`]: struct.EndianFile.html
345    /// [`write_to(self, &mut EndianFile)`]: #method.write_to
346    fn size(&self) -> u32;
347
348    /// Writes this `Datablock` to an [`EndianFile`]. The number of bytes
349    /// written must be exactly same number as returned by [`size(&self)`].
350    ///
351    /// # Panics
352    ///
353    /// Failing to write the exact same number of bytes as indicated in
354    /// [`size(&self)`] will `panic`.
355    ///
356    /// [`EndianFile`]: struct.EndianFile.html
357    /// [`size(&self)`]: #method.size
358    fn write_to(self, file: &mut EndianFile) -> io::Result<()>;
359}
360
361/// [`Datablock`] that consists of a list of bytes.
362///
363/// It is possible to store any block of data in a `ByteBlock`,
364/// but that would require to know the [`Endianness`] of the file
365/// beforehand, so the bytes are written in the correct order.
366///
367/// Using a [`Datablock`], on the other hand, allows to make use
368/// of the functionality of an [`EndianFile`], so the data can be
369/// written without worrying about the endianness.
370///
371/// # Examples
372///
373/// Creating a ByteBlock from a `Vec<u8>`:
374/// ```
375/// use tiff_encoder::prelude::*;
376///
377/// // A vector holding arbitrary u8 data.
378/// // This is the data we want to store as a Byteblock.
379/// let data_8bits: Vec<u8> = vec![0; 65536];
380///
381/// // Create an Offsets of a single Byteblock from the buffer.
382/// // This is the value that can be used directly as an IFD entry value.
383/// let byte_block = ByteBlock::single(data_8bits);
384/// ```
385///
386/// Creating a ByteBlock from a `Vec<u32>`:
387/// ```
388/// extern crate byteorder;
389/// // Crate byteorder will be used to write 32-bit information in a 8-bit buffer.
390/// use byteorder::{LittleEndian, WriteBytesExt};
391/// use tiff_encoder::prelude::*;
392///
393///
394/// // A vector holding arbitrary u32 data.
395/// // This is the data we want to store as a Byteblock.
396/// let data_32bits: Vec<u32> = vec![0; 65536];
397///
398/// // First, let's store the data in a u8 buffer.
399/// let mut image_bytes = Vec::with_capacity(262144); // 65536*4 (each u32 has a size of 4 bytes)
400/// for val in data_32bits {
401///     // A little endian TIFF file is assumed in this example.
402///     image_bytes.write_u32::<LittleEndian>(val).unwrap();
403/// }
404///
405/// // Create an Offsets of a single Byteblock from the buffer.
406/// // This is the value that can be used directly as an IFD entry value.
407/// let byte_block = ByteBlock::single(image_bytes);
408/// ```
409///
410///
411/// [`Datablock`]: trait.Datablock.html
412/// [`EndianFile`]: struct.EndianFile.html
413/// [`Endianness`]: enum.Endianness.html
414pub struct ByteBlock(pub Vec<u8>);
415impl ByteBlock {
416    /// Constructs an [`Offsets`] of `ByteBlock`s from a vector of
417    /// vectors of bytes.
418    ///
419    /// Each vector of bytes represents one `ByteBlock`.
420    ///
421    /// [`Offsets`]: ifd/values/struct.Offsets.html
422    pub fn offsets(blocks: Vec<Vec<u8>>) -> Offsets<ByteBlock> {
423        Offsets::new(blocks.into_iter().map(|block| ByteBlock(block)).collect())
424    }
425
426    /// Constructs an [`Offsets`] from a vector of bytes.
427    ///
428    /// This vector of bytes represents a single `ByteBlock`.
429    ///
430    /// [`Offsets`]: ifd/values/struct.Offsets.html
431    pub fn single(block: Vec<u8>) -> Offsets<ByteBlock> {
432        ByteBlock::offsets(vec![block])
433    }
434}
435impl Datablock for ByteBlock {
436    fn size(&self) -> u32 {
437        self.0.len() as u32
438    }
439
440    fn write_to(self, file: &mut EndianFile) -> io::Result<()> {
441        file.write_all_u8(&self.0)?;
442        Ok(())
443    }
444}