tiff/encoder/
mod.rs

1pub use tiff_value::*;
2
3use std::{
4    cmp,
5    io::{self, Seek, Write},
6    marker::PhantomData,
7    mem,
8    num::{NonZeroU64, TryFromIntError},
9};
10
11use crate::{
12    decoder::ifd::Entry,
13    error::{TiffResult, UsageError},
14    tags::{CompressionMethod, IfdPointer, ResolutionUnit, SampleFormat, Tag, Type},
15    Directory, TiffError, TiffFormatError,
16};
17
18pub mod colortype;
19pub mod compression;
20mod tiff_value;
21mod writer;
22
23use self::colortype::*;
24use self::compression::Compression as Comp;
25use self::compression::*;
26use self::writer::*;
27
28/// Type of prediction to prepare the image with.
29///
30/// Image data can be very unpredictable, and thus very hard to compress. Predictors are simple
31/// passes ran over the image data to prepare it for compression. This is mostly used for LZW
32/// compression, where using [Predictor::Horizontal] we see a 35% improvement in compression
33/// ratio over the unpredicted compression !
34///
35/// [Predictor::FloatingPoint] is currently not supported.
36pub type Predictor = crate::tags::Predictor;
37#[cfg(feature = "deflate")]
38pub type DeflateLevel = compression::DeflateLevel;
39
40#[derive(Clone, Copy, PartialEq)]
41pub enum Compression {
42    Uncompressed,
43    #[cfg(feature = "lzw")]
44    Lzw,
45    #[cfg(feature = "deflate")]
46    Deflate(DeflateLevel),
47    Packbits,
48}
49
50impl Default for Compression {
51    fn default() -> Self {
52        Self::Uncompressed
53    }
54}
55
56impl Compression {
57    fn tag(&self) -> CompressionMethod {
58        match self {
59            Compression::Uncompressed => CompressionMethod::None,
60            #[cfg(feature = "lzw")]
61            Compression::Lzw => CompressionMethod::LZW,
62            #[cfg(feature = "deflate")]
63            Compression::Deflate(_) => CompressionMethod::Deflate,
64            Compression::Packbits => CompressionMethod::PackBits,
65        }
66    }
67
68    fn get_algorithm(&self) -> Compressor {
69        match self {
70            Compression::Uncompressed => compression::Uncompressed {}.get_algorithm(),
71            #[cfg(feature = "lzw")]
72            Compression::Lzw => compression::Lzw {}.get_algorithm(),
73            #[cfg(feature = "deflate")]
74            Compression::Deflate(level) => compression::Deflate::with_level(*level).get_algorithm(),
75            Compression::Packbits => compression::Packbits {}.get_algorithm(),
76        }
77    }
78}
79
80/// Encoder for Tiff and BigTiff files.
81///
82/// With this type you can get a `DirectoryEncoder` or a `ImageEncoder`
83/// to encode Tiff/BigTiff ifd directories with images.
84///
85/// See `DirectoryEncoder` and `ImageEncoder`.
86///
87/// # Examples
88/// ```
89/// # extern crate tiff;
90/// # fn main() {
91/// # let mut file = std::io::Cursor::new(Vec::new());
92/// # let image_data = vec![0; 100*100*3];
93/// use tiff::encoder::*;
94///
95/// // create a standard Tiff file
96/// let mut tiff = TiffEncoder::new(&mut file).unwrap();
97/// tiff.write_image::<colortype::RGB8>(100, 100, &image_data).unwrap();
98///
99/// // create a BigTiff file
100/// let mut bigtiff = TiffEncoder::new_big(&mut file).unwrap();
101/// bigtiff.write_image::<colortype::RGB8>(100, 100, &image_data).unwrap();
102///
103/// # }
104/// ```
105pub struct TiffEncoder<W, K: TiffKind = TiffKindStandard> {
106    writer: TiffWriter<W>,
107    kind: PhantomData<K>,
108    predictor: Predictor,
109    compression: Compression,
110    /// The offset of the last main image directory's `next` field.
111    last_ifd_chain: NonZeroU64,
112}
113
114/// Constructor functions to create standard Tiff files.
115impl<W: Write + Seek> TiffEncoder<W> {
116    /// Creates a new encoder for standard Tiff files.
117    ///
118    /// To create BigTiff files, use [`new_big`][TiffEncoder::new_big] or
119    /// [`new_generic`][TiffEncoder::new_generic].
120    pub fn new(writer: W) -> TiffResult<TiffEncoder<W, TiffKindStandard>> {
121        TiffEncoder::new_generic(writer)
122    }
123}
124
125/// Constructor functions to create BigTiff files.
126impl<W: Write + Seek> TiffEncoder<W, TiffKindBig> {
127    /// Creates a new encoder for BigTiff files.
128    ///
129    /// To create standard Tiff files, use [`new`][TiffEncoder::new] or
130    /// [`new_generic`][TiffEncoder::new_generic].
131    pub fn new_big(writer: W) -> TiffResult<Self> {
132        TiffEncoder::new_generic(writer)
133    }
134}
135
136/// Generic functions that are available for both Tiff and BigTiff encoders.
137impl<W: Write + Seek, K: TiffKind> TiffEncoder<W, K> {
138    /// Creates a new Tiff or BigTiff encoder, inferred from the return type.
139    pub fn new_generic(writer: W) -> TiffResult<Self> {
140        let mut writer = TiffWriter::new(writer);
141        K::write_header(&mut writer)?;
142
143        let last_ifd_chain = NonZeroU64::new(writer.previous_ifd_pointer::<K>())
144            .expect("Header is at a non-zero offset");
145
146        Ok(TiffEncoder {
147            writer,
148            kind: PhantomData,
149            predictor: Predictor::None,
150            compression: Compression::Uncompressed,
151            last_ifd_chain,
152        })
153    }
154
155    /// Set the predictor to use
156    ///
157    /// A predictor is used to simplify the file before writing it. This is very
158    /// useful when writing a file compressed using LZW as it can improve efficiency
159    pub fn with_predictor(mut self, predictor: Predictor) -> Self {
160        self.predictor = predictor;
161
162        self
163    }
164
165    /// Set the compression method to use
166    pub fn with_compression(mut self, compression: Compression) -> Self {
167        self.compression = compression;
168
169        self
170    }
171
172    /// Create a [`DirectoryEncoder`] to encode an ifd directory.
173    #[deprecated = "`image_directory` replaced the old behavior and clarifies the intent"]
174    #[doc(hidden)]
175    pub fn new_directory(&mut self) -> TiffResult<DirectoryEncoder<'_, W, K>> {
176        Self::chain_directory(&mut self.writer, &mut self.last_ifd_chain)
177    }
178
179    /// Create a [`DirectoryEncoder`] to encode an ifd directory.
180    ///
181    /// The caller is responsible for ensuring that the directory is a valid image in the main TIFF
182    /// IFD sequence. To encode additional directories that are not linked into the sequence, use
183    /// [`Self::extra_directory`][TiffEncoder::extra_directory].
184    pub fn image_directory(&mut self) -> TiffResult<DirectoryEncoder<'_, W, K>> {
185        Self::chain_directory(&mut self.writer, &mut self.last_ifd_chain)
186    }
187
188    /// Create a [`DirectoryEncoder`] to encode an ifd directory.
189    ///
190    /// The directory is not linked into the sequence of directories. For instance, encode Exif
191    /// directories or SubIfd directories with this method.
192    pub fn extra_directory(&mut self) -> TiffResult<DirectoryEncoder<'_, W, K>> {
193        Self::unchained_directory(&mut self.writer)
194    }
195
196    /// Create an [`ImageEncoder`] to encode an image one slice at a time.
197    pub fn new_image<C: ColorType>(
198        &mut self,
199        width: u32,
200        height: u32,
201    ) -> TiffResult<ImageEncoder<'_, W, C, K>> {
202        let encoder = Self::chain_directory(&mut self.writer, &mut self.last_ifd_chain)?;
203        ImageEncoder::new(encoder, width, height, self.compression, self.predictor)
204    }
205
206    /// Convenience function to write an entire image from memory.
207    pub fn write_image<C: ColorType>(
208        &mut self,
209        width: u32,
210        height: u32,
211        data: &[C::Inner],
212    ) -> TiffResult<()>
213    where
214        [C::Inner]: TiffValue,
215    {
216        let encoder = Self::chain_directory(&mut self.writer, &mut self.last_ifd_chain)?;
217        let image: ImageEncoder<W, C, K> =
218            ImageEncoder::new(encoder, width, height, self.compression, self.predictor)?;
219        image.write_data(data)
220    }
221
222    fn chain_directory<'lt>(
223        writer: &'lt mut TiffWriter<W>,
224        last_ifd_chain: &'lt mut NonZeroU64,
225    ) -> TiffResult<DirectoryEncoder<'lt, W, K>> {
226        let last_ifd = *last_ifd_chain;
227        DirectoryEncoder::new(writer, Some(last_ifd), Some(last_ifd_chain))
228    }
229
230    fn unchained_directory(writer: &mut TiffWriter<W>) -> TiffResult<DirectoryEncoder<'_, W, K>> {
231        DirectoryEncoder::new(writer, None, None)
232    }
233}
234
235/// Low level interface to encode ifd directories.
236///
237/// You should call `finish` on this when you are finished with it.
238/// Encoding can silently fail while this is dropping.
239pub struct DirectoryEncoder<'a, W: 'a + Write + Seek, K: TiffKind> {
240    writer: &'a mut TiffWriter<W>,
241    /// The position of the previous directory's `next` field, if any.
242    chained_ifd_pos: Option<NonZeroU64>,
243    /// An output to write the `next` field offset on completion.
244    write_chain: Option<&'a mut NonZeroU64>,
245    kind: PhantomData<K>,
246    // We use BTreeMap to make sure tags are written in correct order
247    directory: Directory,
248    dropped: bool,
249}
250
251/// The offset of an encoded directory in the file.
252#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
253pub struct DirectoryOffset<K: TiffKind> {
254    /// The start of the directory as a Tiff value.
255    ///
256    /// This is a bit of a wart in the strongly typed design of the encoder. The value _type_ must
257    /// itself know how to be represented in the Tiff file but that may differ based on endianess
258    /// as well as the offset size (`u32` or BigTIFF's `u64`). Thankfully we're allowed to
259    /// represent offsets with `LONG` or `IFD` in the usual case.
260    pub offset: K::OffsetType,
261    /// The start of the directory as a pure offset.
262    pub pointer: IfdPointer,
263    /// The offset of its sequence link field, in our private representation.
264    ifd_chain: NonZeroU64,
265    /// The kind of Tiff file the offset is for.
266    kind: PhantomData<K>,
267}
268
269impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
270    /// Construct a directory writer appending to data, assuming the writer is currently positioned
271    /// immediately after a previously written IFD to which to append.
272    fn new(
273        writer: &'a mut TiffWriter<W>,
274        chained_ifd_pos: Option<NonZeroU64>,
275        chain_into: Option<&'a mut NonZeroU64>,
276    ) -> TiffResult<Self> {
277        writer.pad_word_boundary()?; // TODO: Do we need to adjust this for BigTiff?
278        Ok(Self {
279            writer,
280            chained_ifd_pos,
281            write_chain: chain_into,
282            kind: PhantomData,
283            directory: Directory::empty(),
284            dropped: false,
285        })
286    }
287
288    /// Write a single ifd tag.
289    pub fn write_tag<T: TiffValue>(&mut self, tag: Tag, value: T) -> TiffResult<()> {
290        // Encodes the value if necessary. In the current bytes all integers are taken as native
291        // endian and thus transparent but this keeps the interface generic.
292        let mut bytes = Vec::with_capacity(value.bytes());
293        {
294            let mut writer = TiffWriter::new(&mut bytes);
295            value.write(&mut writer)?;
296        }
297
298        let entry = Self::write_value(
299            self.writer,
300            &DirectoryEntry {
301                data_type: <T>::FIELD_TYPE,
302                count: value.count().try_into()?,
303                data: bytes,
304            },
305        )?;
306
307        self.directory.extend([(tag, entry)]);
308
309        Ok(())
310    }
311
312    /// Write some data to the tiff file, the offset of the data is returned.
313    ///
314    /// This could be used to write tiff strips.
315    pub fn write_data<T: TiffValue>(&mut self, value: T) -> TiffResult<u64> {
316        let offset = self.writer.offset();
317        value.write(self.writer)?;
318        Ok(offset)
319    }
320
321    /// Define the parent directory.
322    ///
323    /// Each directory has an offset based link to its successor, forming a linked list in the
324    /// file. The encoder writes its own offset into the parent's field when [`Self::finish`] is
325    /// called or it is dropped. This redefines the parent. An offset representation of the parent
326    /// must be acquired by finishing it with [`Self::finish_with_offsets`].
327    pub fn set_parent(&mut self, offset: &DirectoryOffset<K>) {
328        self.chained_ifd_pos = Some(offset.ifd_chain);
329    }
330
331    /// Write out the ifd directory itself.
332    pub fn finish(mut self) -> TiffResult<()> {
333        self.finish_internal()?;
334        Ok(())
335    }
336
337    pub fn finish_with_offsets(mut self) -> TiffResult<DirectoryOffset<K>> {
338        self.finish_internal()
339    }
340
341    fn write_directory(&mut self) -> TiffResult<u64> {
342        // Start by turning all buffered unwritten values into entries.
343        let offset = self.writer.offset();
344        K::write_entry_count(self.writer, self.directory.len())?;
345
346        let offset_bytes = mem::size_of::<K::OffsetType>();
347        for (tag, entry) in self.directory.iter() {
348            self.writer.write_u16(tag.to_u16())?;
349            self.writer.write_u16(entry.field_type().to_u16())?;
350            let count = K::convert_offset(entry.count())?;
351            count.write(self.writer)?;
352            self.writer.write_bytes(&entry.offset()[..offset_bytes])?;
353        }
354
355        Ok(offset)
356    }
357
358    fn write_value(
359        writer: &mut TiffWriter<W>,
360        value: &DirectoryEntry<K::OffsetType>,
361    ) -> TiffResult<Entry> {
362        let &DirectoryEntry {
363            data: ref bytes,
364            ref count,
365            data_type,
366        } = value;
367
368        let in_entry_bytes = mem::size_of::<K::OffsetType>();
369        let mut offset_bytes = [0; 8];
370
371        if bytes.len() > in_entry_bytes {
372            let offset = writer.offset();
373            writer.write_bytes(bytes)?;
374
375            let offset = K::convert_offset(offset)?;
376            offset_bytes[..offset.bytes()].copy_from_slice(&offset.data());
377        } else {
378            // Note: we have indicated our native byte order in the header, hence this
379            // corresponds to our byte order no matter the value type.
380            offset_bytes[..bytes.len()].copy_from_slice(bytes);
381        }
382
383        // Undoing some hidden type. Offset is either u32 or u64. Due to the trait API being public
384        // and some oversight, we can not clone the `count: K::OffsetType` and thus not convert it.
385        // Instead, write it to a buffer...
386        let mut count_bytes = [0; 8];
387        // Nominally Cow but we only expect `Cow::Borrowed`.
388        count_bytes[..count.bytes()].copy_from_slice(&count.data());
389
390        Ok(if in_entry_bytes == 4 {
391            let count = u32::from_ne_bytes(count_bytes[..4].try_into().unwrap());
392            Entry::new(data_type, count, offset_bytes[..4].try_into().unwrap())
393        } else {
394            debug_assert_eq!(in_entry_bytes, 8);
395            let count = u64::from_ne_bytes(count_bytes);
396            Entry::new_u64(data_type, count, offset_bytes)
397        })
398    }
399
400    /// Provides the number of bytes written by the underlying TiffWriter during the last call.
401    fn last_written(&self) -> u64 {
402        self.writer.last_written()
403    }
404
405    fn finish_internal(&mut self) -> TiffResult<DirectoryOffset<K>> {
406        let ifd_pointer = self.write_directory()?;
407        let offset = K::convert_offset(ifd_pointer)?;
408
409        if let Some(prior) = self.chained_ifd_pos {
410            let curr_pos = self.writer.offset();
411
412            self.writer.goto_offset(prior.get())?;
413            // Note how we are not writing the `offset` type itself here as doing so would need to
414            // go through the `TiffValue` trait—the type is not constrained by much. But for the
415            // trait we need a `TiffWriter` which comes with a bunch of additional state such as
416            // compressor etc. that we have no need for.
417            K::write_offset(self.writer, ifd_pointer)?;
418
419            self.writer.goto_offset(curr_pos)?;
420        }
421
422        K::write_offset(self.writer, 0)?;
423
424        let ifd_chain = NonZeroU64::new(self.writer.previous_ifd_pointer::<K>())
425            .expect("IFD chain field is at a non-zero offset");
426
427        if let Some(prior) = self.write_chain.take() {
428            *prior = ifd_chain;
429        }
430
431        self.dropped = true;
432
433        Ok(DirectoryOffset {
434            pointer: IfdPointer(ifd_pointer),
435            offset,
436            ifd_chain,
437            kind: PhantomData,
438        })
439    }
440}
441
442impl<'a, W: Write + Seek, K: TiffKind> Drop for DirectoryEncoder<'a, W, K> {
443    fn drop(&mut self) {
444        if !self.dropped {
445            let _ = self.finish_internal();
446        }
447    }
448}
449
450/// Type to encode images strip by strip.
451///
452/// You should call `finish` on this when you are finished with it.
453/// Encoding can silently fail while this is dropping.
454///
455/// # Examples
456/// ```
457/// # extern crate tiff;
458/// # fn main() {
459/// # let mut file = std::io::Cursor::new(Vec::new());
460/// # let image_data = vec![0; 100*100*3];
461/// use tiff::encoder::*;
462/// use tiff::tags::Tag;
463///
464/// let mut tiff = TiffEncoder::new(&mut file).unwrap();
465/// let mut image = tiff.new_image::<colortype::RGB8>(100, 100).unwrap();
466///
467/// // You can encode tags here
468/// image.encoder().write_tag(Tag::Artist, "Image-tiff").unwrap();
469///
470/// // Strip size can be configured before writing data
471/// image.rows_per_strip(2).unwrap();
472///
473/// let mut idx = 0;
474/// while image.next_strip_sample_count() > 0 {
475///     let sample_count = image.next_strip_sample_count() as usize;
476///     image.write_strip(&image_data[idx..idx+sample_count]).unwrap();
477///     idx += sample_count;
478/// }
479/// image.finish().unwrap();
480/// # }
481/// ```
482/// You can also call write_data function wich will encode by strip and finish
483pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType, K: TiffKind> {
484    encoder: DirectoryEncoder<'a, W, K>,
485    strip_idx: u64,
486    strip_count: u64,
487    row_samples: u64,
488    width: u32,
489    height: u32,
490    rows_per_strip: u64,
491    strip_offsets: Vec<K::OffsetType>,
492    strip_byte_count: Vec<K::OffsetType>,
493    dropped: bool,
494    compression: Compression,
495    predictor: Predictor,
496    _phantom: ::std::marker::PhantomData<C>,
497}
498
499impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind> ImageEncoder<'a, W, T, K> {
500    fn sanity_check(compression: Compression, predictor: Predictor) -> TiffResult<()> {
501        match (predictor, compression, T::SAMPLE_FORMAT[0]) {
502            (Predictor::Horizontal, _, SampleFormat::IEEEFP | SampleFormat::Void) => {
503                Err(TiffError::UsageError(UsageError::PredictorIncompatible))
504            }
505            (Predictor::FloatingPoint, _, _) => {
506                Err(TiffError::UsageError(UsageError::PredictorUnavailable))
507            }
508            _ => Ok(()),
509        }
510    }
511
512    fn new(
513        mut encoder: DirectoryEncoder<'a, W, K>,
514        width: u32,
515        height: u32,
516        compression: Compression,
517        predictor: Predictor,
518    ) -> TiffResult<Self> {
519        if width == 0 || height == 0 {
520            return Err(TiffError::FormatError(TiffFormatError::InvalidDimensions(
521                width, height,
522            )));
523        }
524
525        Self::sanity_check(compression, predictor)?;
526
527        let row_samples = u64::from(width) * u64::try_from(<T>::BITS_PER_SAMPLE.len())?;
528        let row_bytes = row_samples * u64::from(<T::Inner>::BYTE_LEN);
529
530        // Limit the strip size to prevent potential memory and security issues.
531        // Also keep the multiple strip handling 'oiled'
532        let rows_per_strip = {
533            match compression.tag() {
534                CompressionMethod::PackBits => 1, // Each row must be packed separately. Do not compress across row boundaries
535                _ => 1_000_000_u64.div_ceil(row_bytes),
536            }
537        };
538
539        let strip_count = u64::from(height).div_ceil(rows_per_strip);
540
541        encoder.write_tag(Tag::ImageWidth, width)?;
542        encoder.write_tag(Tag::ImageLength, height)?;
543        encoder.write_tag(Tag::Compression, compression.tag().to_u16())?;
544        encoder.write_tag(Tag::Predictor, predictor.to_u16())?;
545
546        encoder.write_tag(Tag::BitsPerSample, <T>::BITS_PER_SAMPLE)?;
547        let sample_format: Vec<_> = <T>::SAMPLE_FORMAT.iter().map(|s| s.to_u16()).collect();
548        encoder.write_tag(Tag::SampleFormat, &sample_format[..])?;
549        encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE.to_u16())?;
550
551        encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?;
552
553        encoder.write_tag(
554            Tag::SamplesPerPixel,
555            u16::try_from(<T>::BITS_PER_SAMPLE.len())?,
556        )?;
557        encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 })?;
558        encoder.write_tag(Tag::YResolution, Rational { n: 1, d: 1 })?;
559        encoder.write_tag(Tag::ResolutionUnit, ResolutionUnit::None.to_u16())?;
560
561        Ok(ImageEncoder {
562            encoder,
563            strip_count,
564            strip_idx: 0,
565            row_samples,
566            rows_per_strip,
567            width,
568            height,
569            strip_offsets: Vec::new(),
570            strip_byte_count: Vec::new(),
571            dropped: false,
572            compression,
573            predictor,
574            _phantom: ::std::marker::PhantomData,
575        })
576    }
577
578    /// Number of samples the next strip should have.
579    pub fn next_strip_sample_count(&self) -> u64 {
580        if self.strip_idx >= self.strip_count {
581            return 0;
582        }
583
584        let raw_start_row = self.strip_idx * self.rows_per_strip;
585        let start_row = cmp::min(u64::from(self.height), raw_start_row);
586        let end_row = cmp::min(u64::from(self.height), raw_start_row + self.rows_per_strip);
587
588        (end_row - start_row) * self.row_samples
589    }
590
591    /// Write a single strip.
592    pub fn write_strip(&mut self, value: &[T::Inner]) -> TiffResult<()>
593    where
594        [T::Inner]: TiffValue,
595    {
596        let samples = self.next_strip_sample_count();
597        if u64::try_from(value.len())? != samples {
598            return Err(io::Error::new(
599                io::ErrorKind::InvalidData,
600                "Slice is wrong size for strip",
601            )
602            .into());
603        }
604
605        // Write the (possible compressed) data to the encoder.
606        let offset = match self.predictor {
607            Predictor::None => self.encoder.write_data(value)?,
608            Predictor::Horizontal => {
609                let mut row_result = Vec::with_capacity(value.len());
610                for row in value.chunks_exact(self.row_samples as usize) {
611                    T::horizontal_predict(row, &mut row_result);
612                }
613                self.encoder.write_data(row_result.as_slice())?
614            }
615            _ => unimplemented!(),
616        };
617
618        let byte_count = self.encoder.last_written() as usize;
619
620        self.strip_offsets.push(K::convert_offset(offset)?);
621        self.strip_byte_count.push(byte_count.try_into()?);
622
623        self.strip_idx += 1;
624        Ok(())
625    }
626
627    /// Write strips from data
628    pub fn write_data(mut self, data: &[T::Inner]) -> TiffResult<()>
629    where
630        [T::Inner]: TiffValue,
631    {
632        let num_pix = usize::try_from(self.width)?
633            .checked_mul(usize::try_from(self.height)?)
634            .ok_or_else(|| {
635                io::Error::new(
636                    io::ErrorKind::InvalidInput,
637                    "Image width * height exceeds usize",
638                )
639            })?;
640        if data.len() < num_pix {
641            return Err(io::Error::new(
642                io::ErrorKind::InvalidData,
643                "Input data slice is undersized for provided dimensions",
644            )
645            .into());
646        }
647
648        self.encoder
649            .writer
650            .set_compression(self.compression.get_algorithm());
651
652        let mut idx = 0;
653        while self.next_strip_sample_count() > 0 {
654            let sample_count = usize::try_from(self.next_strip_sample_count())?;
655            self.write_strip(&data[idx..idx + sample_count])?;
656            idx += sample_count;
657        }
658
659        self.encoder.writer.reset_compression();
660        self.finish()?;
661        Ok(())
662    }
663
664    /// Set image resolution
665    pub fn resolution(&mut self, unit: ResolutionUnit, value: Rational) {
666        self.encoder
667            .write_tag(Tag::ResolutionUnit, unit.to_u16())
668            .unwrap();
669        self.encoder
670            .write_tag(Tag::XResolution, value.clone())
671            .unwrap();
672        self.encoder.write_tag(Tag::YResolution, value).unwrap();
673    }
674
675    /// Set image resolution unit
676    pub fn resolution_unit(&mut self, unit: ResolutionUnit) {
677        self.encoder
678            .write_tag(Tag::ResolutionUnit, unit.to_u16())
679            .unwrap();
680    }
681
682    /// Set image x-resolution
683    pub fn x_resolution(&mut self, value: Rational) {
684        self.encoder.write_tag(Tag::XResolution, value).unwrap();
685    }
686
687    /// Set image y-resolution
688    pub fn y_resolution(&mut self, value: Rational) {
689        self.encoder.write_tag(Tag::YResolution, value).unwrap();
690    }
691
692    /// Set image number of lines per strip
693    ///
694    /// This function needs to be called before any calls to `write_data` or
695    /// `write_strip` and will return an error otherwise.
696    pub fn rows_per_strip(&mut self, value: u32) -> TiffResult<()> {
697        if self.strip_idx != 0 {
698            return Err(io::Error::new(
699                io::ErrorKind::InvalidInput,
700                "Cannot change strip size after data was written",
701            )
702            .into());
703        }
704        // Write tag as 32 bits
705        self.encoder.write_tag(Tag::RowsPerStrip, value)?;
706
707        let value: u64 = value as u64;
708        self.strip_count = (self.height as u64).div_ceil(value);
709        self.rows_per_strip = value;
710
711        Ok(())
712    }
713
714    fn finish_internal(&mut self) -> TiffResult<DirectoryOffset<K>> {
715        self.encoder
716            .write_tag(Tag::StripOffsets, K::convert_slice(&self.strip_offsets))?;
717        self.encoder.write_tag(
718            Tag::StripByteCounts,
719            K::convert_slice(&self.strip_byte_count),
720        )?;
721        self.dropped = true;
722
723        self.encoder.finish_internal()
724    }
725
726    /// Get a reference of the underlying `DirectoryEncoder`
727    pub fn encoder(&mut self) -> &mut DirectoryEncoder<'a, W, K> {
728        &mut self.encoder
729    }
730
731    /// Write out image and ifd directory.
732    pub fn finish(mut self) -> TiffResult<()> {
733        self.finish_internal()?;
734        Ok(())
735    }
736}
737
738impl<'a, W: Write + Seek, C: ColorType, K: TiffKind> Drop for ImageEncoder<'a, W, C, K> {
739    fn drop(&mut self) {
740        if !self.dropped {
741            let _ = self.finish_internal();
742        }
743    }
744}
745
746struct DirectoryEntry<S> {
747    data_type: Type,
748    count: S,
749    data: Vec<u8>,
750}
751
752/// Trait to abstract over Tiff/BigTiff differences.
753///
754/// Implemented for [`TiffKindStandard`] and [`TiffKindBig`].
755pub trait TiffKind {
756    /// The type of offset fields, `u32` for normal Tiff, `u64` for BigTiff.
757    type OffsetType: TryFrom<usize, Error = TryFromIntError> + Into<u64> + TiffValue;
758
759    /// Needed for the `convert_slice` method.
760    type OffsetArrayType: ?Sized + TiffValue;
761
762    /// Write the (Big)Tiff header.
763    fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()>;
764
765    /// Convert a file offset to `Self::OffsetType`.
766    ///
767    /// This returns an error for normal Tiff if the offset is larger than `u32::MAX`.
768    fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType>;
769
770    /// Write an offset value to the given writer.
771    ///
772    /// Like `convert_offset`, this errors if `offset > u32::MAX` for normal Tiff.
773    fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()>;
774
775    /// Write the IFD entry count field with the given `count` value.
776    ///
777    /// The entry count field is an `u16` for normal Tiff and `u64` for BigTiff. Errors
778    /// if the given `usize` is larger than the representable values.
779    fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()>;
780
781    /// Internal helper method for satisfying Rust's type checker.
782    ///
783    /// The `TiffValue` trait is implemented for both primitive values (e.g. `u8`, `u32`) and
784    /// slices of primitive values (e.g. `[u8]`, `[u32]`). However, this is not represented in
785    /// the type system, so there is no guarantee that that for all `T: TiffValue` there is also
786    /// an implementation of `TiffValue` for `[T]`. This method works around that problem by
787    /// providing a conversion from `[T]` to some value that implements `TiffValue`, thereby
788    /// making all slices of `OffsetType` usable with `write_tag` and similar methods.
789    ///
790    /// Implementations of this trait should always set `OffsetArrayType` to `[OffsetType]`.
791    fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType;
792}
793
794/// Create a standard Tiff file.
795pub struct TiffKindStandard;
796
797impl TiffKind for TiffKindStandard {
798    type OffsetType = u32;
799    type OffsetArrayType = [u32];
800
801    fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
802        write_tiff_header(writer)?;
803        // blank the IFD offset location
804        writer.write_u32(0)?;
805
806        Ok(())
807    }
808
809    fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType> {
810        Ok(Self::OffsetType::try_from(offset)?)
811    }
812
813    fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()> {
814        writer.write_u32(u32::try_from(offset)?)?;
815        Ok(())
816    }
817
818    fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()> {
819        writer.write_u16(u16::try_from(count)?)?;
820
821        Ok(())
822    }
823
824    fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType {
825        slice
826    }
827}
828
829/// Create a BigTiff file.
830pub struct TiffKindBig;
831
832impl TiffKind for TiffKindBig {
833    type OffsetType = u64;
834    type OffsetArrayType = [u64];
835
836    fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
837        write_bigtiff_header(writer)?;
838        // blank the IFD offset location
839        writer.write_u64(0)?;
840
841        Ok(())
842    }
843
844    fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType> {
845        Ok(offset)
846    }
847
848    fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()> {
849        writer.write_u64(offset)?;
850        Ok(())
851    }
852
853    fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()> {
854        writer.write_u64(u64::try_from(count)?)?;
855        Ok(())
856    }
857
858    fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType {
859        slice
860    }
861}