Repository: image-rs/image-tiff
Pull request #62 (2019-11-08 11:42:20)

Multipage image patch
While using image-tiff for multipage image I ran into 2 things:
1. the ifd offset is written twice for each image, which results into that the ifd offset of the second and later images was 0 and followed by the correct location.
2. the returned offset of the TiffWriter was 2 bytes off, starting at the second page. The easiest way to resolve this was by using the seek(SeekFrom::Current(0)) functionality of the writer object, instead of fixing the offset housekeeping.




Patch:
diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs
index 96ba414..ab626cf 100644
--- a/src/encoder/mod.rs
+++ b/src/encoder/mod.rs
@@ -1,6 +1,7 @@
 use byteorder::NativeEndian;
 use std::collections::BTreeMap;
 use std::io::{Seek, Write};
+use std::mem;
 
 use decoder::ifd::{self, Tag};
 use error::{TiffError, TiffFormatError, TiffResult};
@@ -351,6 +352,8 @@ impl<W: Write + Seek> TiffEncoder<W> {
         };
 
         NativeEndian::write_header(&mut encoder.writer)?;
+        // blank the IFD offset location
+        encoder.writer.write_u32(0)?;
 
         Ok(encoder)
     }
@@ -417,9 +420,9 @@ pub struct DirectoryEncoder<'a, W: 'a + Write + Seek> {
 
 impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
     fn new(writer: &'a mut TiffWriter<W>) -> TiffResult<DirectoryEncoder<'a, W>> {
+        // the previous word is the IFD offset position
+        let ifd_pointer_pos = writer.offset() - mem::size_of::<u32>() as u64;
         writer.pad_word_boundary()?;
-        let ifd_pointer_pos = writer.offset();
-        writer.write_u32(0)?;
         Ok(DirectoryEncoder {
             writer,
             dropped: false,
diff --git a/src/encoder/writer.rs b/src/encoder/writer.rs
index 78a128d..ba4c4c5 100644
--- a/src/encoder/writer.rs
+++ b/src/encoder/writer.rs
@@ -98,6 +98,7 @@ impl<W: Write> TiffWriter<W> {
             let padding = [0, 0, 0];
             let padd_len = 4 - (self.offset % 4);
             self.writer.write_all(&padding[..padd_len as usize])?;
+            self.offset += padd_len;
         }
 
         Ok(())


Test patch:
diff --git a/tests/encode_images.rs b/tests/encode_images.rs
index 2ae8c1c..6256cec 100644
--- a/tests/encode_images.rs
+++ b/tests/encode_images.rs
@@ -260,4 +260,35 @@ fn test_signed() {
         assert_eq!(decoder.get_tag(Tag::Unknown(65030)).unwrap().into_i32_vec().unwrap(), [-1, 100]);
         assert_eq!(decoder.get_tag(Tag::Unknown(65031)).unwrap().into_i32_vec().unwrap(), [-1_i32, 100, 2, 100]);
     }
-}
\ No newline at end of file
+}
+
+#[test]
+/// check multipage image handling
+fn test_multipage_image() {
+    let mut img_file = Cursor::new(Vec::new());
+
+    {
+        // first create a multipage image with 2 images
+        let mut img_encoder = TiffEncoder::new(&mut img_file).unwrap();
+
+        // write first grayscale image (2x2 16-bit)
+        let img1: Vec<u16> = [1, 2, 3, 4].to_vec();
+        img_encoder.write_image::<colortype::Gray16>(2, 2, &img1[..]);
+        // write second grayscale image (3x3 8-bit)
+        let img2: Vec<u8> = [9, 8, 7, 6, 5, 4, 3, 2, 1].to_vec();
+        img_encoder.write_image::<colortype::Gray8>(3, 3, &img2[..]);
+    }
+
+    // seek to the beginning of the file, so that it can be decoded
+    img_file.seek(SeekFrom::Start(0)).unwrap();
+
+    {
+        let mut img_decoder = Decoder::new(&mut img_file).unwrap();
+
+        // check the dimensions of the image in the first page
+        assert_eq!(img_decoder.dimensions().unwrap(), (2, 2));
+        img_decoder.next_image().unwrap();
+        // check the dimensions of the image in the second page
+        assert_eq!(img_decoder.dimensions().unwrap(), (3, 3));
+    }
+}