Skip to main content

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::{
15        ByteOrder, CompressionMethod, ExtraSamples, IfdPointer, PhotometricInterpretation,
16        ResolutionUnit, SampleFormat, Tag, Type, ValueBuffer,
17    },
18    Directory, TiffError, TiffFormatError,
19};
20
21pub mod colortype;
22pub mod compression;
23mod tiff_value;
24mod writer;
25
26use self::colortype::*;
27use self::compression::Compression as Comp;
28use self::compression::*;
29use self::writer::*;
30
31/// Type of prediction to prepare the image with.
32///
33/// Image data can be very unpredictable, and thus very hard to compress. Predictors are simple
34/// passes ran over the image data to prepare it for compression. This is mostly used for LZW
35/// compression, where using [Predictor::Horizontal] we see a 35% improvement in compression
36/// ratio over the unpredicted compression !
37///
38/// [Predictor::FloatingPoint] is currently not supported.
39pub type Predictor = crate::tags::Predictor;
40#[cfg(feature = "deflate")]
41pub type DeflateLevel = compression::DeflateLevel;
42
43#[derive(Clone, Copy, PartialEq, Default)]
44pub enum Compression {
45    #[default]
46    Uncompressed,
47    #[cfg(feature = "lzw")]
48    Lzw,
49    #[cfg(feature = "deflate")]
50    Deflate(DeflateLevel),
51    Packbits,
52}
53
54impl Compression {
55    fn tag(&self) -> CompressionMethod {
56        match self {
57            Compression::Uncompressed => CompressionMethod::None,
58            #[cfg(feature = "lzw")]
59            Compression::Lzw => CompressionMethod::LZW,
60            #[cfg(feature = "deflate")]
61            Compression::Deflate(_) => CompressionMethod::Deflate,
62            Compression::Packbits => CompressionMethod::PackBits,
63        }
64    }
65
66    fn get_algorithm(&self) -> Compressor {
67        match self {
68            Compression::Uncompressed => compression::Uncompressed {}.get_algorithm(),
69            #[cfg(feature = "lzw")]
70            Compression::Lzw => compression::Lzw {}.get_algorithm(),
71            #[cfg(feature = "deflate")]
72            Compression::Deflate(level) => compression::Deflate::with_level(*level).get_algorithm(),
73            Compression::Packbits => compression::Packbits {}.get_algorithm(),
74        }
75    }
76}
77
78/// Encoder for Tiff and BigTiff files.
79///
80/// With this type you can get a [`DirectoryEncoder`] or a [`ImageEncoder`]
81/// to encode Tiff/BigTiff ifd directories with images.
82///
83/// See [`DirectoryEncoder`] and [`ImageEncoder`].
84///
85/// # Examples
86/// ```
87/// # extern crate tiff;
88/// # fn main() {
89/// # let mut file = std::io::Cursor::new(Vec::new());
90/// # let image_data = vec![0; 100*100*3];
91/// use tiff::encoder::*;
92///
93/// // create a standard Tiff file
94/// let mut tiff = TiffEncoder::new(&mut file).unwrap();
95/// tiff.write_image::<colortype::RGB8>(100, 100, &image_data).unwrap();
96///
97/// // create a BigTiff file
98/// let mut bigtiff = TiffEncoder::new_big(&mut file).unwrap();
99/// bigtiff.write_image::<colortype::RGB8>(100, 100, &image_data).unwrap();
100///
101/// # }
102/// ```
103pub struct TiffEncoder<W, K: TiffKind = TiffKindStandard> {
104    writer: TiffWriter<W>,
105    kind: PhantomData<K>,
106    predictor: Predictor,
107    compression: Compression,
108    /// The offset of the last main image directory's `next` field.
109    last_ifd_chain: NonZeroU64,
110}
111
112/// Constructor functions to create standard Tiff files.
113impl<W: Write + Seek> TiffEncoder<W> {
114    /// Creates a new encoder for standard Tiff files.
115    ///
116    /// To create BigTiff files, use [`new_big`][TiffEncoder::new_big] or
117    /// [`new_generic`][TiffEncoder::new_generic].
118    pub fn new(writer: W) -> TiffResult<TiffEncoder<W, TiffKindStandard>> {
119        TiffEncoder::new_generic(writer)
120    }
121}
122
123/// Constructor functions to create BigTiff files.
124impl<W: Write + Seek> TiffEncoder<W, TiffKindBig> {
125    /// Creates a new encoder for BigTiff files.
126    ///
127    /// To create standard Tiff files, use [`new`][TiffEncoder::new] or
128    /// [`new_generic`][TiffEncoder::new_generic].
129    pub fn new_big(writer: W) -> TiffResult<Self> {
130        TiffEncoder::new_generic(writer)
131    }
132}
133
134/// Generic functions that are available for both Tiff and BigTiff encoders.
135impl<W: Write + Seek, K: TiffKind> TiffEncoder<W, K> {
136    /// Creates a new Tiff or BigTiff encoder, inferred from the return type.
137    pub fn new_generic(writer: W) -> TiffResult<Self> {
138        let mut writer = TiffWriter::new(writer);
139        K::write_header(&mut writer)?;
140
141        let last_ifd_chain = NonZeroU64::new(writer.previous_ifd_pointer::<K>())
142            .expect("Header is at a non-zero offset");
143
144        Ok(TiffEncoder {
145            writer,
146            kind: PhantomData,
147            predictor: Predictor::None,
148            compression: Compression::Uncompressed,
149            last_ifd_chain,
150        })
151    }
152
153    /// Set the predictor to use
154    ///
155    /// A predictor is used to simplify the file before writing it. This is very
156    /// useful when writing a file compressed using LZW as it can improve efficiency
157    pub fn with_predictor(mut self, predictor: Predictor) -> Self {
158        self.predictor = predictor;
159
160        self
161    }
162
163    /// Set the compression method to use
164    pub fn with_compression(mut self, compression: Compression) -> Self {
165        self.compression = compression;
166
167        self
168    }
169
170    /// Create a [`DirectoryEncoder`] to encode an ifd directory.
171    #[deprecated = "`image_directory` replaced the old behavior and clarifies the intent"]
172    #[doc(hidden)]
173    pub fn new_directory(&mut self) -> TiffResult<DirectoryEncoder<'_, W, K>> {
174        Self::chain_directory(&mut self.writer, &mut self.last_ifd_chain)
175    }
176
177    /// Create a [`DirectoryEncoder`] to encode an ifd (directory).
178    ///
179    /// The caller is responsible for ensuring that the directory is a valid image in the main TIFF
180    /// IFD sequence. To encode additional directories that are not linked into the sequence, use
181    /// [`Self::extra_directory`][TiffEncoder::extra_directory].
182    pub fn image_directory(&mut self) -> TiffResult<DirectoryEncoder<'_, W, K>> {
183        Self::chain_directory(&mut self.writer, &mut self.last_ifd_chain)
184    }
185
186    /// Create a [`DirectoryEncoder`] to encode a non-image directory.
187    ///
188    /// The directory is not linked into the sequence of directories. For instance, encode Exif
189    /// directories or SubIfd directories with this method.
190    pub fn extra_directory(&mut self) -> TiffResult<DirectoryEncoder<'_, W, K>> {
191        Self::unchained_directory(&mut self.writer)
192    }
193
194    /// Create an [`ImageEncoder`] to encode an image one slice at a time.
195    pub fn new_image<C: ColorType>(
196        &mut self,
197        width: u32,
198        height: u32,
199    ) -> TiffResult<ImageEncoder<'_, W, C, K>> {
200        let encoder = Self::chain_directory(&mut self.writer, &mut self.last_ifd_chain)?;
201        ImageEncoder::new(encoder, width, height, self.compression, self.predictor)
202    }
203
204    /// Convenience function to write an entire image from memory.
205    pub fn write_image<C: ColorType>(
206        &mut self,
207        width: u32,
208        height: u32,
209        data: &[C::Inner],
210    ) -> TiffResult<()>
211    where
212        [C::Inner]: TiffValue,
213    {
214        let encoder = Self::chain_directory(&mut self.writer, &mut self.last_ifd_chain)?;
215        let image: ImageEncoder<W, C, K> =
216            ImageEncoder::new(encoder, width, height, self.compression, self.predictor)?;
217        image.write_data(data)
218    }
219
220    fn chain_directory<'lt>(
221        writer: &'lt mut TiffWriter<W>,
222        last_ifd_chain: &'lt mut NonZeroU64,
223    ) -> TiffResult<DirectoryEncoder<'lt, W, K>> {
224        let last_ifd = *last_ifd_chain;
225        DirectoryEncoder::new(writer, Some(last_ifd), Some(last_ifd_chain))
226    }
227
228    fn unchained_directory(writer: &mut TiffWriter<W>) -> TiffResult<DirectoryEncoder<'_, W, K>> {
229        DirectoryEncoder::new(writer, None, None)
230    }
231}
232
233/// Low level interface to encode ifd directories.
234///
235/// You should call `finish` on this when you are finished with it.
236/// Encoding can silently fail while this is dropping.
237pub struct DirectoryEncoder<'a, W: 'a + Write + Seek, K: TiffKind> {
238    writer: &'a mut TiffWriter<W>,
239    /// The position of the previous directory's `next` field, if any.
240    chained_ifd_pos: Option<NonZeroU64>,
241    /// An output to write the `next` field offset on completion.
242    write_chain: Option<&'a mut NonZeroU64>,
243    kind: PhantomData<K>,
244    // We use BTreeMap to make sure tags are written in correct order
245    directory: Directory,
246    dropped: bool,
247}
248
249/// The offset of an encoded directory in the file.
250///
251/// Both the `offset` and `pointer` field are a complete description of the start offset of the
252/// dictionary which is used to point to it. The struct also describes the extent covered by the
253/// encoding of the directory. The constructors [`new`][DirectoryOffset::new] allow the checked
254/// conversion. Note that even though the offsets are public do not expect their information to
255/// propagate when modified.
256#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
257pub struct DirectoryOffset<K: TiffKind> {
258    /// The start of the directory as a Tiff value.
259    ///
260    /// This is a bit of a wart in the strongly typed design of the encoder. The value _type_ must
261    /// itself know how to be represented in the Tiff file but that may differ based on endianess
262    /// as well as the offset size (`u32` or BigTIFF's `u64`). Thankfully we're allowed to
263    /// represent offsets with `LONG` or `IFD` in the usual case.
264    pub offset: K::OffsetType,
265    /// The start of the directory as a pure offset.
266    pub pointer: IfdPointer,
267    /// The offset of its sequence link field, in our private representation. Before exposing this
268    /// make sure that we don't want any invariants (e.g. pointer being smaller than this). The
269    /// type should be fine.
270    ifd_chain: NonZeroU64,
271    /// The kind of Tiff file the offset is for.
272    kind: PhantomData<K>,
273}
274
275impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
276    /// Construct a directory writer appending to data, assuming the writer is currently positioned
277    /// immediately after a previously written IFD to which to append.
278    fn new(
279        writer: &'a mut TiffWriter<W>,
280        chained_ifd_pos: Option<NonZeroU64>,
281        chain_into: Option<&'a mut NonZeroU64>,
282    ) -> TiffResult<Self> {
283        writer.pad_word_boundary()?; // TODO: Do we need to adjust this for BigTiff?
284        Ok(Self {
285            writer,
286            chained_ifd_pos,
287            write_chain: chain_into,
288            kind: PhantomData,
289            directory: Directory::empty(),
290            dropped: false,
291        })
292    }
293
294    /// Write a single ifd tag.
295    pub fn write_tag<T: TiffValue>(&mut self, tag: Tag, value: T) -> TiffResult<()> {
296        // Encodes the value if necessary. In the current bytes all integers are taken as native
297        // endian and thus transparent but this keeps the interface generic.
298        let mut bytes = Vec::with_capacity(value.bytes());
299        {
300            let mut writer = TiffWriter::new(&mut bytes);
301            value.write(&mut writer)?;
302        }
303
304        let entry = Self::write_entry_inner(
305            self.writer,
306            DirectoryEntry {
307                data_type: <T>::FIELD_TYPE,
308                count: value.count().try_into()?,
309                data: bytes.into(),
310            },
311        )?;
312
313        self.directory.extend([(tag, entry)]);
314
315        Ok(())
316    }
317
318    /// Write a tag-value pair with prepared byte data.
319    ///
320    /// Note that the library will _not_ attempt to verify that the data type or the count of the
321    /// buffered value is permissible for the given tag. The data will be associated with the tag
322    /// exactly as-is.
323    ///
324    /// An error is returned if the byte order of the presented value does not match the byte order
325    /// of the underlying file (i.e. the [native byte order](`crate::tags::ByteOrder::native`)).
326    pub fn write_tag_buf(&mut self, tag: Tag, value: &ValueBuffer) -> TiffResult<()> {
327        let entry = self.write_entry_buf(value)?;
328        self.directory.extend([(tag, entry)]);
329
330        Ok(())
331    }
332
333    /// Write some data to the tiff file, the offset of the data is returned.
334    ///
335    /// This could be used to write tiff strips. All of the data will be written into the file as a
336    /// slice of bytes. It can not be used as an entry in a directory directly, where very short
337    /// values are instead represented in the offset field directly. Use [`Self::write_entry`]
338    /// instead.
339    pub fn write_data<T: TiffValue>(&mut self, value: T) -> TiffResult<u64> {
340        let offset = self.writer.offset();
341        value.write(self.writer)?;
342        Ok(offset)
343    }
344
345    /// Write some data directly to the tiff file, the offset of the data is returned.
346    ///
347    /// All of the data will be written into the file as a slice of bytes. It can not be used as an
348    /// entry in a directory directly, where very short values are instead represented in the
349    /// offset field directly. Use [`Self::write_entry_buf`] instead.
350    ///
351    /// An error is returned if the byte order of the presented value does not match the byte order
352    /// of the underlying file (i.e. the [native byte order](`crate::tags::ByteOrder::native`)).
353    pub fn write_data_buf(&mut self, value: &ValueBuffer) -> TiffResult<u64> {
354        self.check_value_byteorder(value)?;
355        let offset = self.writer.offset();
356        self.writer.write_bytes(value.as_bytes())?;
357        Ok(offset)
358    }
359
360    /// Write a directory value into the file.
361    ///
362    /// Returns an [`Entry`]. If the value is short enough it will *not* be written in the file but
363    /// stored in the entry's offset field.
364    pub fn write_entry<T: TiffValue>(&mut self, value: T) -> TiffResult<Entry> {
365        Self::write_entry_inner(
366            self.writer,
367            DirectoryEntry {
368                data_type: T::FIELD_TYPE,
369                count: K::convert_offset(value.count() as u64)?,
370                // FIXME: not optimal we always allocate here. But the only other method we have
371                // available is `T::write(&mut TiffWriter<W>)` and we must pass that into
372                // `write_entry_inner` but then have a combinatorial explosion of instantiations
373                // which is absurd.
374                data: value.data(),
375            },
376        )
377    }
378
379    /// Write a directory value into the file.
380    ///
381    /// An error is returned if the byte order of the presented value does not match the byte order
382    /// of the underlying file (i.e. the [native byte order](`crate::tags::ByteOrder::native`)).
383    ///
384    /// Returns an [`Entry`]. If the value is short enough it will *not* be written in the file but
385    /// stored in the entry's offset field.
386    pub fn write_entry_buf(&mut self, value: &ValueBuffer) -> TiffResult<Entry> {
387        self.check_value_byteorder(value)?;
388
389        Self::write_entry_inner(
390            self.writer,
391            DirectoryEntry {
392                data_type: value.data_type(),
393                count: K::convert_offset(value.count())?,
394                data: value.as_bytes().into(),
395            },
396        )
397    }
398
399    /// Write a directory entry by its byte data.
400    ///
401    /// The data must be pre-formatted to the byte order expected by the file.
402    ///
403    /// Returns an [`Entry`]. If the value is short enough it will *not* be written in the file but
404    /// stored in the entry's offset field.
405    pub fn write_entry_bytes(&mut self, ty: Type, data: &[u8]) -> TiffResult<Entry> {
406        if data.len() % usize::from(ty.byte_len()) != 0 {
407            return Err(TiffError::UsageError(UsageError::MismatchedEntryLength {
408                ty,
409                found: data.len(),
410            }));
411        }
412
413        let count = data.len() / usize::from(ty.byte_len());
414        let count = u64::try_from(count)?;
415
416        Self::write_entry_inner(
417            self.writer,
418            DirectoryEntry {
419                data_type: ty,
420                count: K::convert_offset(count)?,
421                data: data.into(),
422            },
423        )
424    }
425
426    /// Insert previously written key-value entries.
427    ///
428    /// It is the caller's responsibility to ensure that the data referred to by the entries is
429    /// actually (or will be) written to the file. Note that small values are necessarily inline in
430    /// the entry, the size limit depends on the Tiff kind. So do not confuse a directory from a
431    /// BigTiff in a standard tiff or the other way around.
432    pub fn extend_from(&mut self, dir: &Directory) {
433        let entries = dir.iter().map(|(tag, val)| (tag, val.clone()));
434        self.directory.extend(entries);
435    }
436
437    /// Define the parent directory.
438    ///
439    /// Each directory has an offset based link to its successor, forming a linked list in the
440    /// file. The encoder writes its own offset into the parent's field when [`Self::finish`] is
441    /// called or it is dropped. This redefines the parent. An offset representation of the parent
442    /// must be acquired by finishing it with [`Self::finish_with_offsets`].
443    pub fn set_parent(&mut self, offset: &DirectoryOffset<K>) {
444        self.chained_ifd_pos = Some(offset.ifd_chain);
445    }
446
447    /// Write out the ifd directory itself.
448    pub fn finish(mut self) -> TiffResult<()> {
449        self.finish_internal()?;
450        Ok(())
451    }
452
453    /// Write the IFD to the file and return the offset it is written to.
454    ///
455    /// The offset can be used as the value, or as part of a list of values, in the entry of
456    /// another directory. If you're constructing the entry directly you'll want to use an
457    /// appropriate type variant for this such as [`Type::IFD`].
458    pub fn finish_with_offsets(mut self) -> TiffResult<DirectoryOffset<K>> {
459        self.finish_internal()
460    }
461
462    fn write_directory(&mut self) -> TiffResult<u64> {
463        // Start by turning all buffered unwritten values into entries.
464        let offset = self.writer.offset();
465        K::write_entry_count(self.writer, self.directory.len())?;
466
467        let offset_bytes = mem::size_of::<K::OffsetType>();
468        for (tag, entry) in self.directory.iter() {
469            self.writer.write_u16(tag.to_u16())?;
470            self.writer.write_u16(entry.field_type().to_u16())?;
471            let count = K::convert_offset(entry.count())?;
472            count.write(self.writer)?;
473            self.writer.write_bytes(&entry.offset()[..offset_bytes])?;
474        }
475
476        Ok(offset)
477    }
478
479    // Does it really make sense that this would be a (potentially) compressible write? For actual
480    // directory values their size must match the data written so there's going to be a silent
481    // problem if that actually occurs.
482    fn write_entry_inner(
483        writer: &mut TiffWriter<W>,
484        value: DirectoryEntry<K::OffsetType>,
485    ) -> TiffResult<Entry> {
486        let DirectoryEntry {
487            data: ref bytes,
488            ref count,
489            data_type,
490        } = value;
491
492        let in_entry_bytes = mem::size_of::<K::OffsetType>();
493
494        let mut offset_bytes = [0; 8];
495
496        if bytes.len() > in_entry_bytes {
497            let offset = writer.offset();
498            writer.write_bytes(bytes)?;
499
500            let offset = K::convert_offset(offset)?;
501            offset_bytes[..offset.bytes()].copy_from_slice(&offset.data());
502        } else {
503            // Note: we have indicated our native byte order in the header, hence this
504            // corresponds to our byte order no matter the value type.
505            offset_bytes[..bytes.len()].copy_from_slice(bytes);
506        }
507
508        // Undoing some hidden type. Offset is either u32 or u64. Due to the trait API being public
509        // and some oversight, we can not clone the `count: K::OffsetType` and thus not convert it.
510        // Instead, write it to a buffer...
511        let mut count_bytes = [0; 8];
512        debug_assert!(in_entry_bytes == 4 || in_entry_bytes == 8);
513        // Nominally Cow but we only expect `Cow::Borrowed`.
514        count_bytes[..count.bytes()].copy_from_slice(&count.data());
515
516        Ok(if in_entry_bytes == 4 {
517            let count = u32::from_ne_bytes(count_bytes[..4].try_into().unwrap());
518            Entry::new(data_type, count, offset_bytes[..4].try_into().unwrap())
519        } else {
520            debug_assert_eq!(in_entry_bytes, 8);
521            let count = u64::from_ne_bytes(count_bytes);
522            Entry::new_u64(data_type, count, offset_bytes)
523        })
524    }
525
526    fn check_value_byteorder(&self, value: &ValueBuffer) -> TiffResult<()> {
527        // FIXME: we should enable writing files with any chosen byte order.
528        if value.byte_order() != ByteOrder::native() {
529            Err(TiffError::UsageError(UsageError::ByteOrderMismatch))
530        } else {
531            Ok(())
532        }
533    }
534
535    /// Provides the number of bytes written by the underlying TiffWriter during the last call.
536    fn last_written(&self) -> u64 {
537        self.writer.last_written()
538    }
539
540    fn finish_internal(&mut self) -> TiffResult<DirectoryOffset<K>> {
541        let ifd_pointer = self.write_directory()?;
542        let offset = K::convert_offset(ifd_pointer)?;
543
544        if let Some(prior) = self.chained_ifd_pos {
545            let curr_pos = self.writer.offset();
546
547            self.writer.goto_offset(prior.get())?;
548            // Note how we are not writing the `offset` type itself here as doing so would need to
549            // go through the `TiffValue` trait—the type is not constrained by much. But for the
550            // trait we need a `TiffWriter` which comes with a bunch of additional state such as
551            // compressor etc. that we have no need for.
552            K::write_offset(self.writer, ifd_pointer)?;
553
554            self.writer.goto_offset(curr_pos)?;
555        }
556
557        K::write_offset(self.writer, 0)?;
558
559        let ifd_chain = NonZeroU64::new(self.writer.previous_ifd_pointer::<K>())
560            .expect("IFD chain field is at a non-zero offset");
561
562        if let Some(prior) = self.write_chain.take() {
563            *prior = ifd_chain;
564        }
565
566        self.dropped = true;
567
568        Ok(DirectoryOffset {
569            pointer: IfdPointer(ifd_pointer),
570            offset,
571            ifd_chain,
572            kind: PhantomData,
573        })
574    }
575}
576
577impl<'a, W: Write + Seek, K: TiffKind> Drop for DirectoryEncoder<'a, W, K> {
578    fn drop(&mut self) {
579        if !self.dropped {
580            let _ = self.finish_internal();
581        }
582    }
583}
584
585/// Type to encode images strip by strip.
586///
587/// You should call `finish` on this when you are finished with it.
588/// Encoding can silently fail while this is dropping.
589///
590/// # Examples
591/// ```
592/// # extern crate tiff;
593/// # fn main() {
594/// # let mut file = std::io::Cursor::new(Vec::new());
595/// # let image_data = vec![0; 100*100*3];
596/// use tiff::encoder::*;
597/// use tiff::tags::Tag;
598///
599/// let mut tiff = TiffEncoder::new(&mut file).unwrap();
600/// let mut image = tiff.new_image::<colortype::RGB8>(100, 100).unwrap();
601///
602/// // You can encode tags here
603/// image.encoder().write_tag(Tag::Artist, "Image-tiff").unwrap();
604///
605/// // Strip size can be configured before writing data
606/// image.rows_per_strip(2).unwrap();
607///
608/// let mut idx = 0;
609/// while image.next_strip_sample_count() > 0 {
610///     let sample_count = image.next_strip_sample_count() as usize;
611///     image.write_strip(&image_data[idx..idx+sample_count]).unwrap();
612///     idx += sample_count;
613/// }
614/// image.finish().unwrap();
615/// # }
616/// ```
617/// You can also call write_data function wich will encode by strip and finish
618pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType, K: TiffKind> {
619    encoder: DirectoryEncoder<'a, W, K>,
620    strip_idx: u64,
621    strip_count: u64,
622    row_samples: u64,
623    width: u32,
624    height: u32,
625    extra_samples: Vec<u16>,
626    rows_per_strip: u64,
627    strip_offsets: Vec<K::OffsetType>,
628    strip_byte_count: Vec<K::OffsetType>,
629    dropped: bool,
630    compression: Compression,
631    predictor: Predictor,
632    _phantom: ::std::marker::PhantomData<C>,
633}
634
635impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind> ImageEncoder<'a, W, T, K> {
636    fn sanity_check(compression: Compression, predictor: Predictor) -> TiffResult<()> {
637        match (predictor, compression, T::SAMPLE_FORMAT[0]) {
638            (Predictor::Horizontal, _, SampleFormat::IEEEFP | SampleFormat::Void) => {
639                Err(TiffError::UsageError(UsageError::PredictorIncompatible))
640            }
641            (Predictor::FloatingPoint, _, _) => {
642                Err(TiffError::UsageError(UsageError::PredictorUnavailable))
643            }
644            _ => Ok(()),
645        }
646    }
647
648    fn new(
649        mut encoder: DirectoryEncoder<'a, W, K>,
650        width: u32,
651        height: u32,
652        compression: Compression,
653        predictor: Predictor,
654    ) -> TiffResult<Self> {
655        if width == 0 || height == 0 {
656            return Err(TiffError::FormatError(TiffFormatError::InvalidDimensions(
657                width, height,
658            )));
659        }
660
661        Self::sanity_check(compression, predictor)?;
662
663        let row_samples = u64::from(width) * u64::try_from(<T>::BITS_PER_SAMPLE.len())?;
664        let row_bytes = row_samples * u64::from(<T::Inner>::BYTE_LEN);
665
666        // Limit the strip size to prevent potential memory and security issues.
667        // Also keep the multiple strip handling 'oiled'
668        let rows_per_strip = {
669            match compression.tag() {
670                CompressionMethod::PackBits => 1, // Each row must be packed separately. Do not compress across row boundaries
671                _ => 1_000_000_u64.div_ceil(row_bytes),
672            }
673        };
674
675        let strip_count = u64::from(height).div_ceil(rows_per_strip);
676
677        encoder.write_tag(Tag::ImageWidth, width)?;
678        encoder.write_tag(Tag::ImageLength, height)?;
679        encoder.write_tag(Tag::Compression, compression.tag().to_u16())?;
680        encoder.write_tag(Tag::Predictor, predictor.to_u16())?;
681
682        encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE.to_u16())?;
683
684        if matches!(<T>::TIFF_VALUE, PhotometricInterpretation::YCbCr) {
685            // The default for this tag is 2,2 for subsampling but we do not support such a
686            // transformation. Instead all samples must be provided.
687            encoder.write_tag(Tag::ChromaSubsampling, &[1u16, 1u16][..])?;
688        }
689
690        encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?;
691
692        encoder.write_tag(
693            Tag::SamplesPerPixel,
694            u16::try_from(<T>::BITS_PER_SAMPLE.len())?,
695        )?;
696        encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 })?;
697        encoder.write_tag(Tag::YResolution, Rational { n: 1, d: 1 })?;
698        encoder.write_tag(Tag::ResolutionUnit, ResolutionUnit::None)?;
699
700        Ok(ImageEncoder {
701            encoder,
702            strip_count,
703            strip_idx: 0,
704            row_samples,
705            rows_per_strip,
706            extra_samples: vec![],
707            width,
708            height,
709            strip_offsets: Vec::new(),
710            strip_byte_count: Vec::new(),
711            dropped: false,
712            compression,
713            predictor,
714            _phantom: ::std::marker::PhantomData,
715        })
716    }
717
718    pub fn extra_samples(&mut self, extra: &[ExtraSamples]) -> Result<(), TiffError> {
719        if self.strip_idx != 0 {
720            return Err(TiffError::UsageError(
721                UsageError::ReconfiguredAfterImageWrite,
722            ));
723        }
724
725        let samples = self.extra_samples.len() + extra.len() + <T>::BITS_PER_SAMPLE.len();
726        let row_samples = u64::from(self.width) * u64::try_from(samples)?;
727
728        self.extra_samples
729            .extend(extra.iter().map(ExtraSamples::to_u16));
730
731        self.encoder
732            .write_tag(Tag::ExtraSamples, &self.extra_samples[..])?;
733
734        self.row_samples = row_samples;
735
736        Ok(())
737    }
738
739    /// Number of samples the next strip should have.
740    pub fn next_strip_sample_count(&self) -> u64 {
741        if self.strip_idx >= self.strip_count {
742            return 0;
743        }
744
745        let raw_start_row = self.strip_idx * self.rows_per_strip;
746        let start_row = cmp::min(u64::from(self.height), raw_start_row);
747        let end_row = cmp::min(u64::from(self.height), raw_start_row + self.rows_per_strip);
748
749        (end_row - start_row) * self.row_samples
750    }
751
752    /// Write a single strip.
753    pub fn write_strip(&mut self, value: &[T::Inner]) -> TiffResult<()>
754    where
755        [T::Inner]: TiffValue,
756    {
757        let samples = self.next_strip_sample_count();
758        if u64::try_from(value.len())? != samples {
759            return Err(io::Error::new(
760                io::ErrorKind::InvalidData,
761                "Slice is wrong size for strip",
762            )
763            .into());
764        }
765
766        // Write the (possible compressed) data to the encoder.
767        let offset = match self.predictor {
768            Predictor::None => self.encoder.write_data(value)?,
769            Predictor::Horizontal => {
770                let mut row_result = Vec::with_capacity(value.len());
771                for row in value.chunks_exact(self.row_samples as usize) {
772                    T::horizontal_predict(row, &mut row_result);
773                }
774                self.encoder.write_data(row_result.as_slice())?
775            }
776            _ => unimplemented!(),
777        };
778
779        let byte_count = self.encoder.last_written() as usize;
780
781        self.strip_offsets.push(K::convert_offset(offset)?);
782        self.strip_byte_count.push(byte_count.try_into()?);
783
784        self.strip_idx += 1;
785        Ok(())
786    }
787
788    /// Write strips from data
789    pub fn write_data(mut self, data: &[T::Inner]) -> TiffResult<()>
790    where
791        [T::Inner]: TiffValue,
792    {
793        let num_pix = usize::try_from(self.width)?
794            .checked_mul(usize::try_from(self.height)?)
795            .ok_or_else(|| {
796                io::Error::new(
797                    io::ErrorKind::InvalidInput,
798                    "Image width * height exceeds usize",
799                )
800            })?;
801        if data.len() < num_pix {
802            return Err(io::Error::new(
803                io::ErrorKind::InvalidData,
804                "Input data slice is undersized for provided dimensions",
805            )
806            .into());
807        }
808
809        self.encoder
810            .writer
811            .set_compression(self.compression.get_algorithm());
812
813        let mut idx = 0;
814        while self.next_strip_sample_count() > 0 {
815            let sample_count = usize::try_from(self.next_strip_sample_count())?;
816            self.write_strip(&data[idx..idx + sample_count])?;
817            idx += sample_count;
818        }
819
820        self.encoder.writer.reset_compression();
821        self.finish()?;
822        Ok(())
823    }
824
825    /// Set image resolution
826    pub fn resolution(&mut self, unit: ResolutionUnit, value: Rational) {
827        self.encoder
828            .write_tag(Tag::ResolutionUnit, unit.to_u16())
829            .unwrap();
830        self.encoder
831            .write_tag(Tag::XResolution, value.clone())
832            .unwrap();
833        self.encoder.write_tag(Tag::YResolution, value).unwrap();
834    }
835
836    /// Set image resolution unit
837    pub fn resolution_unit(&mut self, unit: ResolutionUnit) {
838        self.encoder
839            .write_tag(Tag::ResolutionUnit, unit.to_u16())
840            .unwrap();
841    }
842
843    /// Set image x-resolution
844    pub fn x_resolution(&mut self, value: Rational) {
845        self.encoder.write_tag(Tag::XResolution, value).unwrap();
846    }
847
848    /// Set image y-resolution
849    pub fn y_resolution(&mut self, value: Rational) {
850        self.encoder.write_tag(Tag::YResolution, value).unwrap();
851    }
852
853    /// Set image number of lines per strip
854    ///
855    /// This function needs to be called before any calls to `write_data` or
856    /// `write_strip` and will return an error otherwise.
857    pub fn rows_per_strip(&mut self, value: u32) -> TiffResult<()> {
858        if self.strip_idx != 0 {
859            return Err(io::Error::new(
860                io::ErrorKind::InvalidInput,
861                "Cannot change strip size after data was written",
862            )
863            .into());
864        }
865        // Write tag as 32 bits
866        self.encoder.write_tag(Tag::RowsPerStrip, value)?;
867
868        let value: u64 = value as u64;
869        self.strip_count = (self.height as u64).div_ceil(value);
870        self.rows_per_strip = value;
871
872        Ok(())
873    }
874
875    fn finish_internal(&mut self) -> TiffResult<DirectoryOffset<K>> {
876        if self.extra_samples.is_empty() {
877            self.encoder
878                .write_tag(Tag::BitsPerSample, <T>::BITS_PER_SAMPLE)?;
879        } else {
880            let mut sample_format: Vec<_> = <T>::BITS_PER_SAMPLE.to_vec();
881            let replicated =
882                core::iter::repeat_n(<T>::BITS_PER_SAMPLE[0], self.extra_samples.len());
883            sample_format.extend(replicated);
884
885            self.encoder
886                .write_tag(Tag::BitsPerSample, &sample_format[..])?;
887
888            self.encoder.write_tag(
889                Tag::SamplesPerPixel,
890                u16::try_from(<T>::BITS_PER_SAMPLE.len() + self.extra_samples.len())?,
891            )?;
892        }
893
894        let mut sample_format: Vec<_> = <T>::SAMPLE_FORMAT.iter().map(|s| s.to_u16()).collect();
895        let extra_format = sample_format
896            .first()
897            .copied()
898            // Frankly should not occur, we have no sample format without at least one entry. We
899            // would need an interface upgrade to handle this however. Either decide to support
900            // heterogeneous sample formats or a way to provide fallback that is used for purely
901            // extra samples and not provided via a slice used for samples themselves.
902            .unwrap_or(SampleFormat::Void.to_u16());
903
904        sample_format.extend(core::iter::repeat_n(extra_format, self.extra_samples.len()));
905
906        self.encoder
907            .write_tag(Tag::SampleFormat, &sample_format[..])?;
908
909        self.encoder
910            .write_tag(Tag::StripOffsets, K::convert_slice(&self.strip_offsets))?;
911        self.encoder.write_tag(
912            Tag::StripByteCounts,
913            K::convert_slice(&self.strip_byte_count),
914        )?;
915        self.dropped = true;
916
917        self.encoder.finish_internal()
918    }
919
920    /// Get a reference of the underlying `DirectoryEncoder`
921    pub fn encoder(&mut self) -> &mut DirectoryEncoder<'a, W, K> {
922        &mut self.encoder
923    }
924
925    /// Write out image and ifd directory.
926    pub fn finish(mut self) -> TiffResult<()> {
927        self.finish_internal()?;
928        Ok(())
929    }
930}
931
932impl<'a, W: Write + Seek, C: ColorType, K: TiffKind> Drop for ImageEncoder<'a, W, C, K> {
933    fn drop(&mut self) {
934        if !self.dropped {
935            let _ = self.finish_internal();
936        }
937    }
938}
939
940struct DirectoryEntry<'data, S> {
941    data_type: Type,
942    count: S,
943    data: std::borrow::Cow<'data, [u8]>,
944}
945
946/// Trait to abstract over Tiff/BigTiff differences.
947///
948/// Implemented for [`TiffKindStandard`] and [`TiffKindBig`].
949pub trait TiffKind {
950    /// The type of offset fields, `u32` for normal Tiff, `u64` for BigTiff.
951    type OffsetType: TryFrom<usize, Error = TryFromIntError> + Into<u64> + TiffValue;
952
953    /// Needed for the `convert_slice` method.
954    type OffsetArrayType: ?Sized + TiffValue;
955
956    /// Write the (Big)Tiff header.
957    fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()>;
958
959    /// Convert a file offset to `Self::OffsetType`.
960    ///
961    /// This returns an error for normal Tiff if the offset is larger than `u32::MAX`.
962    fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType>;
963
964    /// Write an offset value to the given writer.
965    ///
966    /// Like `convert_offset`, this errors if `offset > u32::MAX` for normal Tiff.
967    fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()>;
968
969    /// Write the IFD entry count field with the given `count` value.
970    ///
971    /// The entry count field is an `u16` for normal Tiff and `u64` for BigTiff. Errors
972    /// if the given `usize` is larger than the representable values.
973    fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()>;
974
975    /// Internal helper method for satisfying Rust's type checker.
976    ///
977    /// The `TiffValue` trait is implemented for both primitive values (e.g. `u8`, `u32`) and
978    /// slices of primitive values (e.g. `[u8]`, `[u32]`). However, this is not represented in
979    /// the type system, so there is no guarantee that that for all `T: TiffValue` there is also
980    /// an implementation of `TiffValue` for `[T]`. This method works around that problem by
981    /// providing a conversion from `[T]` to some value that implements `TiffValue`, thereby
982    /// making all slices of `OffsetType` usable with `write_tag` and similar methods.
983    ///
984    /// Implementations of this trait should always set `OffsetArrayType` to `[OffsetType]`.
985    fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType;
986}
987
988impl<K: TiffKind> DirectoryOffset<K> {
989    /// Construct the directory pointer information from its raw offset.
990    ///
991    /// Fails in these cases:
992    ///
993    /// - if start offset is too large to be represented with the chosen TIFF format, i.e. a value
994    ///   larger than [`u32::MAX`] with a standard tiff which is only valid in a BigTiff. This
995    ///   returns [`TiffError::IntSizeError`].
996    /// - the extent of the directory would overflow the file length. This returns a
997    ///   [`TiffError::UsageError`].
998    ///
999    /// The library does *not* validate the contents or offset in any way, it just performs the
1000    /// necessary math assuming the data is an accurate representation of existing content.
1001    ///
1002    /// # Examples
1003    ///
1004    /// If you have any custom way of writing a directory to a file that can not go through the
1005    /// usual [`TiffEncoder::extra_directory`] then you can reconstruct the offset information as
1006    /// long as you provide all the necessary data to the library. You can then use it as a parent
1007    /// directory, i.e. have the library overwrite its `next` field.
1008    ///
1009    /// ```
1010    /// use tiff::{
1011    ///     encoder::{TiffEncoder, DirectoryOffset},
1012    ///     Directory,
1013    ///     tags::IfdPointer,
1014    /// };
1015    ///
1016    /// # let mut file = std::io::Cursor::new(Vec::new());
1017    /// let mut tiff = TiffEncoder::new(&mut file)?;
1018    ///
1019    /// // … some custom data writes, assume we know where a directory was written
1020    /// # fn reconstruction_of_your_directory() -> Directory { Directory::from_iter([]) }
1021    /// let known_offset = IfdPointer(1024);
1022    /// let known_contents: Directory = reconstruction_of_your_directory();
1023    ///
1024    /// let reconstructed = DirectoryOffset::new(known_offset, &known_contents)?;
1025    ///
1026    /// // Now we can use it as if the directory was written by the `TiffEncoder` itself.
1027    /// let mut chain = tiff.extra_directory()?;
1028    /// chain.set_parent(&reconstructed);
1029    /// chain.finish_with_offsets()?;
1030    ///
1031    /// # Ok::<_, tiff::TiffError>(())
1032    /// ```
1033    pub fn new(pointer: IfdPointer, dir: &Directory) -> TiffResult<Self> {
1034        let offset = K::convert_offset(pointer.0)?;
1035        let encoded = dir.encoded_len::<K>();
1036        let offset_field_offset = encoded - mem::size_of_val(&offset) as u64;
1037
1038        let ifd_chain = pointer
1039            .0
1040            .checked_add(offset_field_offset)
1041            .and_then(NonZeroU64::new)
1042            .ok_or(TiffError::UsageError(UsageError::ZeroIfdPointer))?;
1043
1044        Ok(DirectoryOffset {
1045            pointer,
1046            offset,
1047            ifd_chain,
1048            kind: PhantomData,
1049        })
1050    }
1051}
1052
1053/// Create a standard Tiff file.
1054pub struct TiffKindStandard;
1055
1056impl TiffKind for TiffKindStandard {
1057    type OffsetType = u32;
1058    type OffsetArrayType = [u32];
1059
1060    fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
1061        write_tiff_header(writer)?;
1062        // blank the IFD offset location
1063        writer.write_u32(0)?;
1064
1065        Ok(())
1066    }
1067
1068    fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType> {
1069        Ok(Self::OffsetType::try_from(offset)?)
1070    }
1071
1072    fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()> {
1073        writer.write_u32(u32::try_from(offset)?)?;
1074        Ok(())
1075    }
1076
1077    fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()> {
1078        writer.write_u16(u16::try_from(count)?)?;
1079
1080        Ok(())
1081    }
1082
1083    fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType {
1084        slice
1085    }
1086}
1087
1088/// Create a BigTiff file.
1089pub struct TiffKindBig;
1090
1091impl TiffKind for TiffKindBig {
1092    type OffsetType = u64;
1093    type OffsetArrayType = [u64];
1094
1095    fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
1096        write_bigtiff_header(writer)?;
1097        // blank the IFD offset location
1098        writer.write_u64(0)?;
1099
1100        Ok(())
1101    }
1102
1103    fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType> {
1104        Ok(offset)
1105    }
1106
1107    fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()> {
1108        writer.write_u64(offset)?;
1109        Ok(())
1110    }
1111
1112    fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()> {
1113        writer.write_u64(u64::try_from(count)?)?;
1114        Ok(())
1115    }
1116
1117    fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType {
1118        slice
1119    }
1120}
1121
1122#[test]
1123fn directory_offset_new_equivalent_to_writing() {
1124    type K = TiffKindStandard;
1125
1126    let dir = Directory::from_iter(vec![
1127        (
1128            Tag::ImageWidth,
1129            Entry::new(Type::LONG, 1, 100u32.to_ne_bytes()),
1130        ),
1131        (
1132            Tag::ImageLength,
1133            Entry::new(Type::LONG, 1, 200u32.to_ne_bytes()),
1134        ),
1135    ]);
1136
1137    let data = std::io::Cursor::new(vec![]);
1138    let mut file = TiffEncoder::new(data).unwrap();
1139
1140    let mut as_dir_encoder = file.extra_directory().unwrap();
1141    as_dir_encoder.extend_from(&dir);
1142    let dir_extent = as_dir_encoder.finish_with_offsets().unwrap();
1143
1144    // Check we can recreate the extent had we written ourselves.
1145    let synth = DirectoryOffset::<K>::new(dir_extent.pointer, &dir).unwrap();
1146
1147    assert_eq!(synth.pointer, dir_extent.pointer);
1148    assert_eq!(synth.offset, dir_extent.offset);
1149    assert_eq!(synth.ifd_chain, dir_extent.ifd_chain);
1150}