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_encoder::write::{Datablock, EndianFile};
404/// use tiff_encoder::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.
481///
482/// # Examples
483///
484/// Creating a ByteBlock from a `Vec<u8>`:
485/// ```
486/// use tiff_encoder::prelude::*;
487///
488/// // A vector holding arbitrary u8 data.
489/// // This is the data we want to store as a Byteblock.
490/// let data_8bits: Vec<u8> = vec![0; 65536];
491///
492/// // Create an Offsets of a single Byteblock from the buffer.
493/// // This is the value that can be used directly as an IFD entry value.
494/// let byte_block = ByteBlock::single(data_8bits);
495/// ```
496///
497/// Creating a ByteBlock from a `Vec<u32>`:
498/// ```
499/// extern crate byteorder;
500/// // Crate byteorder will be used to write 32-bit information in a 8-bit buffer.
501/// use byteorder::{LittleEndian, WriteBytesExt};
502/// use tiff_encoder::prelude::*;
503///
504///
505/// // A vector holding arbitrary u32 data.
506/// // This is the data we want to store as a Byteblock.
507/// let data_32bits: Vec<u32> = vec![0; 65536];
508///
509/// // First, let's store the data in a u8 buffer.
510/// let mut image_bytes = Vec::with_capacity(262144); // 65536*4 (each u32 has a size of 4 bytes)
511/// for val in data_32bits {
512/// // A little endian TIFF file is assumed in this example.
513/// image_bytes.write_u32::<LittleEndian>(val).unwrap();
514/// }
515///
516/// // Create an Offsets of a single Byteblock from the buffer.
517/// // This is the value that can be used directly as an IFD entry value.
518/// let byte_block = ByteBlock::single(image_bytes);
519/// ```
520///
521///
522/// [`Datablock`]: trait.Datablock.html
523/// [`EndianFile`]: struct.EndianFile.html
524/// [`Endianness`]: enum.Endianness.html
525pub struct ByteBlock(pub Vec<u8>);
526impl ByteBlock {
527 /// Constructs an [`Offsets`] of `ByteBlock`s from a vector of
528 /// vectors of bytes.
529 ///
530 /// Each vector of bytes represents one `ByteBlock`.
531 ///
532 /// [`Offsets`]: ifd/values/struct.Offsets.html
533 pub fn offsets(blocks: Vec<Vec<u8>>) -> Offsets<ByteBlock> {
534 Offsets::new(blocks.into_iter().map(|block| ByteBlock(block)).collect())
535 }
536
537 /// Constructs an [`Offsets`] from a vector of bytes.
538 ///
539 /// This vector of bytes represents a single `ByteBlock`.
540 ///
541 /// [`Offsets`]: ifd/values/struct.Offsets.html
542 pub fn single(block: Vec<u8>) -> Offsets<ByteBlock> {
543 ByteBlock::offsets(vec![block])
544 }
545
546 /// Constructs a BigTIFF-compatible [`Offsets`] of `ByteBlock`s from a vector of
547 /// vectors of bytes.
548 ///
549 /// Each vector of bytes represents one `ByteBlock`.
550 ///
551 /// [`Offsets`]: ifd/values/struct.Offsets.html
552 pub fn big_offsets(blocks: Vec<Vec<u8>>) -> Offsets<ByteBlock, u64> {
553 Offsets::new(blocks.into_iter().map(|block| ByteBlock(block)).collect())
554 }
555
556 /// Constructs a BigTIFF-compatible [`Offsets`] from a vector of bytes.
557 ///
558 /// This vector of bytes represents a single `ByteBlock`.
559 ///
560 /// [`Offsets`]: ifd/values/struct.Offsets.html
561 pub fn big_single(block: Vec<u8>) -> Offsets<ByteBlock, u64> {
562 ByteBlock::big_offsets(vec![block])
563 }
564}
565impl Datablock for ByteBlock {
566 fn size(&self) -> u64 {
567 self.0.len() as u64
568 }
569
570 fn write_to(self, file: &mut EndianFile) -> io::Result<()> {
571 file.write_all_u8(&self.0)?;
572 Ok(())
573 }
574}