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}