1pub use tiff_value::*;
2
3use std::{
4 cmp,
5 collections::BTreeMap,
6 convert::{TryFrom, TryInto},
7 io::{self, Seek, Write},
8 marker::PhantomData,
9 mem,
10 num::TryFromIntError,
11};
12
13use crate::{
14 error::TiffResult,
15 tags::{CompressionMethod, ResolutionUnit, Tag},
16 TiffError, TiffFormatError,
17};
18
19pub mod colortype;
20pub mod compression;
21mod tiff_value;
22mod writer;
23
24use self::colortype::*;
25use self::compression::*;
26use self::writer::*;
27
28/// Encoder for Tiff and BigTiff files.
29///
30/// With this type you can get a `DirectoryEncoder` or a `ImageEncoder`
31/// to encode Tiff/BigTiff ifd directories with images.
32///
33/// See `DirectoryEncoder` and `ImageEncoder`.
34///
35/// # Examples
36/// ```
37/// # extern crate tiff;
38/// # fn main() {
39/// # let mut file = std::io::Cursor::new(Vec::new());
40/// # let image_data = vec![0; 100*100*3];
41/// use tiff::encoder::*;
42///
43/// // create a standard Tiff file
44/// let mut tiff = TiffEncoder::new(&mut file).unwrap();
45/// tiff.write_image::<colortype::RGB8>(100, 100, &image_data).unwrap();
46///
47/// // create a BigTiff file
48/// let mut bigtiff = TiffEncoder::new_big(&mut file).unwrap();
49/// bigtiff.write_image::<colortype::RGB8>(100, 100, &image_data).unwrap();
50///
51/// # }
52/// ```
53pub struct TiffEncoder<W, K: TiffKind = TiffKindStandard> {
54 writer: TiffWriter<W>,
55 kind: PhantomData<K>,
56}
57
58/// Constructor functions to create standard Tiff files.
59impl<W: Write + Seek> TiffEncoder<W> {
60 /// Creates a new encoder for standard Tiff files.
61 ///
62 /// To create BigTiff files, use [`new_big`][TiffEncoder::new_big] or
63 /// [`new_generic`][TiffEncoder::new_generic].
64 pub fn new(writer: W) -> TiffResult<TiffEncoder<W, TiffKindStandard>> {
65 TiffEncoder::new_generic(writer)
66 }
67}
68
69/// Constructor functions to create BigTiff files.
70impl<W: Write + Seek> TiffEncoder<W, TiffKindBig> {
71 /// Creates a new encoder for BigTiff files.
72 ///
73 /// To create standard Tiff files, use [`new`][TiffEncoder::new] or
74 /// [`new_generic`][TiffEncoder::new_generic].
75 pub fn new_big(writer: W) -> TiffResult<Self> {
76 TiffEncoder::new_generic(writer)
77 }
78}
79
80/// Generic functions that are available for both Tiff and BigTiff encoders.
81impl<W: Write + Seek, K: TiffKind> TiffEncoder<W, K> {
82 /// Creates a new Tiff or BigTiff encoder, inferred from the return type.
83 pub fn new_generic(writer: W) -> TiffResult<Self> {
84 let mut encoder = TiffEncoder {
85 writer: TiffWriter::new(writer),
86 kind: PhantomData,
87 };
88
89 K::write_header(&mut encoder.writer)?;
90
91 Ok(encoder)
92 }
93
94 /// Create a [`DirectoryEncoder`] to encode an ifd directory.
95 pub fn new_directory(&mut self) -> TiffResult<DirectoryEncoder<W, K>> {
96 DirectoryEncoder::new(&mut self.writer)
97 }
98
99 /// Create an [`ImageEncoder`] to encode an image one slice at a time.
100 pub fn new_image<C: ColorType>(
101 &mut self,
102 width: u32,
103 height: u32,
104 ) -> TiffResult<ImageEncoder<W, C, K, Uncompressed>> {
105 let encoder = DirectoryEncoder::new(&mut self.writer)?;
106 ImageEncoder::new(encoder, width, height)
107 }
108
109 /// Create an [`ImageEncoder`] to encode an image one slice at a time.
110 pub fn new_image_with_compression<C: ColorType, D: Compression>(
111 &mut self,
112 width: u32,
113 height: u32,
114 compression: D,
115 ) -> TiffResult<ImageEncoder<W, C, K, D>> {
116 let encoder = DirectoryEncoder::new(&mut self.writer)?;
117 ImageEncoder::with_compression(encoder, width, height, compression)
118 }
119
120 /// Convenience function to write an entire image from memory.
121 pub fn write_image<C: ColorType>(
122 &mut self,
123 width: u32,
124 height: u32,
125 data: &[C::Inner],
126 ) -> TiffResult<()>
127 where
128 [C::Inner]: TiffValue,
129 {
130 let encoder = DirectoryEncoder::new(&mut self.writer)?;
131 let image: ImageEncoder<W, C, K> = ImageEncoder::new(encoder, width, height)?;
132 image.write_data(data)
133 }
134
135 /// Convenience function to write an entire image from memory with a given compression.
136 pub fn write_image_with_compression<C: ColorType, D: Compression>(
137 &mut self,
138 width: u32,
139 height: u32,
140 compression: D,
141 data: &[C::Inner],
142 ) -> TiffResult<()>
143 where
144 [C::Inner]: TiffValue,
145 {
146 let encoder = DirectoryEncoder::new(&mut self.writer)?;
147 let image: ImageEncoder<W, C, K, D> =
148 ImageEncoder::with_compression(encoder, width, height, compression)?;
149 image.write_data(data)
150 }
151}
152
153/// Low level interface to encode ifd directories.
154///
155/// You should call `finish` on this when you are finished with it.
156/// Encoding can silently fail while this is dropping.
157pub struct DirectoryEncoder<'a, W: 'a + Write + Seek, K: TiffKind> {
158 writer: &'a mut TiffWriter<W>,
159 dropped: bool,
160 // We use BTreeMap to make sure tags are written in correct order
161 ifd_pointer_pos: u64,
162 ifd: BTreeMap<u16, DirectoryEntry<K::OffsetType>>,
163}
164
165impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
166 fn new(writer: &'a mut TiffWriter<W>) -> TiffResult<Self> {
167 // the previous word is the IFD offset position
168 let ifd_pointer_pos = writer.offset() - mem::size_of::<K::OffsetType>() as u64;
169 writer.pad_word_boundary()?; // TODO: Do we need to adjust this for BigTiff?
170 Ok(DirectoryEncoder {
171 writer,
172 dropped: false,
173 ifd_pointer_pos,
174 ifd: BTreeMap::new(),
175 })
176 }
177
178 /// Write a single ifd tag.
179 pub fn write_tag<T: TiffValue>(&mut self, tag: Tag, value: T) -> TiffResult<()> {
180 let mut bytes = Vec::with_capacity(value.bytes());
181 {
182 let mut writer = TiffWriter::new(&mut bytes);
183 value.write(&mut writer)?;
184 }
185
186 self.ifd.insert(
187 tag.to_u16(),
188 DirectoryEntry {
189 data_type: <T>::FIELD_TYPE.to_u16(),
190 count: value.count().try_into()?,
191 data: bytes,
192 },
193 );
194
195 Ok(())
196 }
197
198 fn write_directory(&mut self) -> TiffResult<u64> {
199 // Start by writing out all values
200 for &mut DirectoryEntry {
201 data: ref mut bytes,
202 ..
203 } in self.ifd.values_mut()
204 {
205 let data_bytes = mem::size_of::<K::OffsetType>();
206
207 if bytes.len() > data_bytes {
208 let offset = self.writer.offset();
209 self.writer.write_bytes(bytes)?;
210 *bytes = vec![0; data_bytes];
211 let mut writer = TiffWriter::new(bytes as &mut [u8]);
212 K::write_offset(&mut writer, offset)?;
213 } else {
214 while bytes.len() < data_bytes {
215 bytes.push(0);
216 }
217 }
218 }
219
220 let offset = self.writer.offset();
221
222 K::write_entry_count(self.writer, self.ifd.len())?;
223 for (
224 tag,
225 DirectoryEntry {
226 data_type: field_type,
227 count,
228 data: offset,
229 },
230 ) in self.ifd.iter()
231 {
232 self.writer.write_u16(*tag)?;
233 self.writer.write_u16(*field_type)?;
234 (*count).write(self.writer)?;
235 self.writer.write_bytes(offset)?;
236 }
237
238 Ok(offset)
239 }
240
241 /// Write some data to the tiff file, the offset of the data is returned.
242 ///
243 /// This could be used to write tiff strips.
244 pub fn write_data<T: TiffValue>(&mut self, value: T) -> TiffResult<u64> {
245 let offset = self.writer.offset();
246 value.write(self.writer)?;
247 Ok(offset)
248 }
249
250 /// Provides the number of bytes written by the underlying TiffWriter during the last call.
251 fn last_written(&self) -> u64 {
252 self.writer.last_written()
253 }
254
255 fn finish_internal(&mut self) -> TiffResult<()> {
256 let ifd_pointer = self.write_directory()?;
257 let curr_pos = self.writer.offset();
258
259 self.writer.goto_offset(self.ifd_pointer_pos)?;
260 K::write_offset(self.writer, ifd_pointer)?;
261 self.writer.goto_offset(curr_pos)?;
262 K::write_offset(self.writer, 0)?;
263
264 self.dropped = true;
265
266 Ok(())
267 }
268
269 /// Write out the ifd directory.
270 pub fn finish(mut self) -> TiffResult<()> {
271 self.finish_internal()
272 }
273}
274
275impl<'a, W: Write + Seek, K: TiffKind> Drop for DirectoryEncoder<'a, W, K> {
276 fn drop(&mut self) {
277 if !self.dropped {
278 let _ = self.finish_internal();
279 }
280 }
281}
282
283/// Type to encode images strip by strip.
284///
285/// You should call `finish` on this when you are finished with it.
286/// Encoding can silently fail while this is dropping.
287///
288/// # Examples
289/// ```
290/// # extern crate tiff;
291/// # fn main() {
292/// # let mut file = std::io::Cursor::new(Vec::new());
293/// # let image_data = vec![0; 100*100*3];
294/// use tiff::encoder::*;
295/// use tiff::tags::Tag;
296///
297/// let mut tiff = TiffEncoder::new(&mut file).unwrap();
298/// let mut image = tiff.new_image::<colortype::RGB8>(100, 100).unwrap();
299///
300/// // You can encode tags here
301/// image.encoder().write_tag(Tag::Artist, "Image-tiff").unwrap();
302///
303/// // Strip size can be configured before writing data
304/// image.rows_per_strip(2).unwrap();
305///
306/// let mut idx = 0;
307/// while image.next_strip_sample_count() > 0 {
308/// let sample_count = image.next_strip_sample_count() as usize;
309/// image.write_strip(&image_data[idx..idx+sample_count]).unwrap();
310/// idx += sample_count;
311/// }
312/// image.finish().unwrap();
313/// # }
314/// ```
315/// You can also call write_data function wich will encode by strip and finish
316pub struct ImageEncoder<
317 'a,
318 W: 'a + Write + Seek,
319 C: ColorType,
320 K: TiffKind,
321 D: Compression = Uncompressed,
322> {
323 encoder: DirectoryEncoder<'a, W, K>,
324 strip_idx: u64,
325 strip_count: u64,
326 row_samples: u64,
327 width: u32,
328 height: u32,
329 rows_per_strip: u64,
330 strip_offsets: Vec<K::OffsetType>,
331 strip_byte_count: Vec<K::OffsetType>,
332 dropped: bool,
333 compression: D,
334 _phantom: ::std::marker::PhantomData<C>,
335}
336
337impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind, D: Compression>
338 ImageEncoder<'a, W, T, K, D>
339{
340 fn new(encoder: DirectoryEncoder<'a, W, K>, width: u32, height: u32) -> TiffResult<Self>
341 where
342 D: Default,
343 {
344 Self::with_compression(encoder, width, height, D::default())
345 }
346
347 fn with_compression(
348 mut encoder: DirectoryEncoder<'a, W, K>,
349 width: u32,
350 height: u32,
351 compression: D,
352 ) -> TiffResult<Self> {
353 if width == 0 || height == 0 {
354 return Err(TiffError::FormatError(TiffFormatError::InvalidDimensions(
355 width, height,
356 )));
357 }
358
359 let row_samples = u64::from(width) * u64::try_from(<T>::BITS_PER_SAMPLE.len())?;
360 let row_bytes = row_samples * u64::from(<T::Inner>::BYTE_LEN);
361
362 // Limit the strip size to prevent potential memory and security issues.
363 // Also keep the multiple strip handling 'oiled'
364 let rows_per_strip = {
365 match D::COMPRESSION_METHOD {
366 CompressionMethod::PackBits => 1, // Each row must be packed separately. Do not compress across row boundaries
367 _ => (1_000_000 + row_bytes - 1) / row_bytes,
368 }
369 };
370
371 let strip_count = (u64::from(height) + rows_per_strip - 1) / rows_per_strip;
372
373 encoder.write_tag(Tag::ImageWidth, width)?;
374 encoder.write_tag(Tag::ImageLength, height)?;
375 encoder.write_tag(Tag::Compression, D::COMPRESSION_METHOD.to_u16())?;
376
377 encoder.write_tag(Tag::BitsPerSample, <T>::BITS_PER_SAMPLE)?;
378 let sample_format: Vec<_> = <T>::SAMPLE_FORMAT.iter().map(|s| s.to_u16()).collect();
379 encoder.write_tag(Tag::SampleFormat, &sample_format[..])?;
380 encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE.to_u16())?;
381
382 encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?;
383
384 encoder.write_tag(
385 Tag::SamplesPerPixel,
386 u16::try_from(<T>::BITS_PER_SAMPLE.len())?,
387 )?;
388 encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 })?;
389 encoder.write_tag(Tag::YResolution, Rational { n: 1, d: 1 })?;
390 encoder.write_tag(Tag::ResolutionUnit, ResolutionUnit::None.to_u16())?;
391
392 Ok(ImageEncoder {
393 encoder,
394 strip_count,
395 strip_idx: 0,
396 row_samples,
397 rows_per_strip,
398 width,
399 height,
400 strip_offsets: Vec::new(),
401 strip_byte_count: Vec::new(),
402 dropped: false,
403 compression,
404 _phantom: ::std::marker::PhantomData,
405 })
406 }
407
408 /// Number of samples the next strip should have.
409 pub fn next_strip_sample_count(&self) -> u64 {
410 if self.strip_idx >= self.strip_count {
411 return 0;
412 }
413
414 let raw_start_row = self.strip_idx * self.rows_per_strip;
415 let start_row = cmp::min(u64::from(self.height), raw_start_row);
416 let end_row = cmp::min(u64::from(self.height), raw_start_row + self.rows_per_strip);
417
418 (end_row - start_row) * self.row_samples
419 }
420
421 /// Write a single strip.
422 pub fn write_strip(&mut self, value: &[T::Inner]) -> TiffResult<()>
423 where
424 [T::Inner]: TiffValue,
425 {
426 let samples = self.next_strip_sample_count();
427 if u64::try_from(value.len())? != samples {
428 return Err(io::Error::new(
429 io::ErrorKind::InvalidData,
430 "Slice is wrong size for strip",
431 )
432 .into());
433 }
434
435 // Write the (possible compressed) data to the encoder.
436 let offset = self.encoder.write_data(value)?;
437 let byte_count = self.encoder.last_written() as usize;
438
439 self.strip_offsets.push(K::convert_offset(offset)?);
440 self.strip_byte_count.push(byte_count.try_into()?);
441
442 self.strip_idx += 1;
443 Ok(())
444 }
445
446 /// Write strips from data
447 pub fn write_data(mut self, data: &[T::Inner]) -> TiffResult<()>
448 where
449 [T::Inner]: TiffValue,
450 {
451 let num_pix = usize::try_from(self.width)?
452 .checked_mul(usize::try_from(self.height)?)
453 .ok_or_else(|| {
454 io::Error::new(
455 io::ErrorKind::InvalidInput,
456 "Image width * height exceeds usize",
457 )
458 })?;
459 if data.len() < num_pix {
460 return Err(io::Error::new(
461 io::ErrorKind::InvalidData,
462 "Input data slice is undersized for provided dimensions",
463 )
464 .into());
465 }
466
467 self.encoder
468 .writer
469 .set_compression(self.compression.get_algorithm());
470
471 let mut idx = 0;
472 while self.next_strip_sample_count() > 0 {
473 let sample_count = usize::try_from(self.next_strip_sample_count())?;
474 self.write_strip(&data[idx..idx + sample_count])?;
475 idx += sample_count;
476 }
477
478 self.encoder.writer.reset_compression();
479 self.finish()?;
480 Ok(())
481 }
482
483 /// Set image resolution
484 pub fn resolution(&mut self, unit: ResolutionUnit, value: Rational) {
485 self.encoder
486 .write_tag(Tag::ResolutionUnit, unit.to_u16())
487 .unwrap();
488 self.encoder
489 .write_tag(Tag::XResolution, value.clone())
490 .unwrap();
491 self.encoder.write_tag(Tag::YResolution, value).unwrap();
492 }
493
494 /// Set image resolution unit
495 pub fn resolution_unit(&mut self, unit: ResolutionUnit) {
496 self.encoder
497 .write_tag(Tag::ResolutionUnit, unit.to_u16())
498 .unwrap();
499 }
500
501 /// Set image x-resolution
502 pub fn x_resolution(&mut self, value: Rational) {
503 self.encoder.write_tag(Tag::XResolution, value).unwrap();
504 }
505
506 /// Set image y-resolution
507 pub fn y_resolution(&mut self, value: Rational) {
508 self.encoder.write_tag(Tag::YResolution, value).unwrap();
509 }
510
511 /// Set image number of lines per strip
512 ///
513 /// This function needs to be called before any calls to `write_data` or
514 /// `write_strip` and will return an error otherwise.
515 pub fn rows_per_strip(&mut self, value: u32) -> TiffResult<()> {
516 if self.strip_idx != 0 {
517 return Err(io::Error::new(
518 io::ErrorKind::InvalidInput,
519 "Cannot change strip size after data was written",
520 )
521 .into());
522 }
523 // Write tag as 32 bits
524 self.encoder.write_tag(Tag::RowsPerStrip, value)?;
525
526 let value: u64 = value as u64;
527 self.strip_count = (self.height as u64 + value - 1) / value;
528 self.rows_per_strip = value;
529
530 Ok(())
531 }
532
533 fn finish_internal(&mut self) -> TiffResult<()> {
534 self.encoder
535 .write_tag(Tag::StripOffsets, K::convert_slice(&self.strip_offsets))?;
536 self.encoder.write_tag(
537 Tag::StripByteCounts,
538 K::convert_slice(&self.strip_byte_count),
539 )?;
540 self.dropped = true;
541
542 self.encoder.finish_internal()
543 }
544
545 /// Get a reference of the underlying `DirectoryEncoder`
546 pub fn encoder(&mut self) -> &mut DirectoryEncoder<'a, W, K> {
547 &mut self.encoder
548 }
549
550 /// Write out image and ifd directory.
551 pub fn finish(mut self) -> TiffResult<()> {
552 self.finish_internal()
553 }
554}
555
556impl<'a, W: Write + Seek, C: ColorType, K: TiffKind, D: Compression> Drop
557 for ImageEncoder<'a, W, C, K, D>
558{
559 fn drop(&mut self) {
560 if !self.dropped {
561 let _ = self.finish_internal();
562 }
563 }
564}
565
566struct DirectoryEntry<S> {
567 data_type: u16,
568 count: S,
569 data: Vec<u8>,
570}
571
572/// Trait to abstract over Tiff/BigTiff differences.
573///
574/// Implemented for [`TiffKindStandard`] and [`TiffKindBig`].
575pub trait TiffKind {
576 /// The type of offset fields, `u32` for normal Tiff, `u64` for BigTiff.
577 type OffsetType: TryFrom<usize, Error = TryFromIntError> + Into<u64> + TiffValue;
578
579 /// Needed for the `convert_slice` method.
580 type OffsetArrayType: ?Sized + TiffValue;
581
582 /// Write the (Big)Tiff header.
583 fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()>;
584
585 /// Convert a file offset to `Self::OffsetType`.
586 ///
587 /// This returns an error for normal Tiff if the offset is larger than `u32::MAX`.
588 fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType>;
589
590 /// Write an offset value to the given writer.
591 ///
592 /// Like `convert_offset`, this errors if `offset > u32::MAX` for normal Tiff.
593 fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()>;
594
595 /// Write the IFD entry count field with the given `count` value.
596 ///
597 /// The entry count field is an `u16` for normal Tiff and `u64` for BigTiff. Errors
598 /// if the given `usize` is larger than the representable values.
599 fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()>;
600
601 /// Internal helper method for satisfying Rust's type checker.
602 ///
603 /// The `TiffValue` trait is implemented for both primitive values (e.g. `u8`, `u32`) and
604 /// slices of primitive values (e.g. `[u8]`, `[u32]`). However, this is not represented in
605 /// the type system, so there is no guarantee that that for all `T: TiffValue` there is also
606 /// an implementation of `TiffValue` for `[T]`. This method works around that problem by
607 /// providing a conversion from `[T]` to some value that implements `TiffValue`, thereby
608 /// making all slices of `OffsetType` usable with `write_tag` and similar methods.
609 ///
610 /// Implementations of this trait should always set `OffsetArrayType` to `[OffsetType]`.
611 fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType;
612}
613
614/// Create a standard Tiff file.
615pub struct TiffKindStandard;
616
617impl TiffKind for TiffKindStandard {
618 type OffsetType = u32;
619 type OffsetArrayType = [u32];
620
621 fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
622 write_tiff_header(writer)?;
623 // blank the IFD offset location
624 writer.write_u32(0)?;
625
626 Ok(())
627 }
628
629 fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType> {
630 Ok(Self::OffsetType::try_from(offset)?)
631 }
632
633 fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()> {
634 writer.write_u32(u32::try_from(offset)?)?;
635 Ok(())
636 }
637
638 fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()> {
639 writer.write_u16(u16::try_from(count)?)?;
640
641 Ok(())
642 }
643
644 fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType {
645 slice
646 }
647}
648
649/// Create a BigTiff file.
650pub struct TiffKindBig;
651
652impl TiffKind for TiffKindBig {
653 type OffsetType = u64;
654 type OffsetArrayType = [u64];
655
656 fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
657 write_bigtiff_header(writer)?;
658 // blank the IFD offset location
659 writer.write_u64(0)?;
660
661 Ok(())
662 }
663
664 fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType> {
665 Ok(offset)
666 }
667
668 fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()> {
669 writer.write_u64(offset)?;
670 Ok(())
671 }
672
673 fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()> {
674 writer.write_u64(u64::try_from(count)?)?;
675 Ok(())
676 }
677
678 fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType {
679 slice
680 }
681}
682