tiff_forge/
write.rs

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