Repository: image-rs/image-tiff (rust)

Issue:
Provide a way to retrieve all tags defined within a file
Currently, it is only possible to retrieve single tags via `get_tag(...)` and `find_tag(...)`. However, one might be interested in obtaining all defined tags, for instance, when modifying the TIFF and writing it to a file again. Two possible APIs come to my mind:
* `fn get_all_tags():  impl<Iterator<Item = Tag>>`, returning all tags defined in the TIFF file, or
* `fn get_all_tags():  HashMap<Tag, ifd::Value>`, returning all tags along with their value

It would be really nice to have this addition.



Pull request description:
Add support for iterating over all tags
Closes #243

Patch:
diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs
index 8b5ffbc..2b8b0dd 100644
--- a/src/decoder/mod.rs
+++ b/src/decoder/mod.rs
@@ -1,17 +1,16 @@
 use std::collections::{HashMap, HashSet};
 use std::io::{self, Read, Seek};
 
+use crate::tags::{
+    CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Predictor, SampleFormat,
+    Tag, Type,
+};
 use crate::{
     bytecast, ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError,
 };
 
 use self::ifd::Directory;
 use self::image::Image;
-use crate::tags::{
-    CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Predictor, SampleFormat,
-    Tag, Type,
-};
-
 use self::stream::{ByteOrder, EndianReader, SmartReader};
 
 pub mod ifd;
@@ -895,6 +894,15 @@ impl<R: Read + Seek> Decoder<R> {
         self.get_tag(tag)?.into_string()
     }
 
+    /// Returns an iterator over all tags in the current image, along with their values.
+    pub fn tag_iter(&mut self) -> impl Iterator<Item = TiffResult<(Tag, ifd::Value)>> + '_ {
+        self.image.ifd.as_ref().unwrap().iter().map(|(tag, entry)| {
+            entry
+                .val(&self.limits, self.bigtiff, &mut self.reader)
+                .map(|value| (*tag, value))
+        })
+    }
+
     fn check_chunk_type(&self, expected: ChunkType) -> TiffResult<()> {
         if expected != self.image().chunk_type {
             return Err(TiffError::UsageError(UsageError::InvalidChunkType(


Test patch:
diff --git a/tests/encode_images.rs b/tests/encode_images.rs
index 532ed21..ee1e3c0 100644
--- a/tests/encode_images.rs
+++ b/tests/encode_images.rs
@@ -36,6 +36,47 @@ fn encode_decode() {
         let mut decoder = Decoder::new(&mut file).unwrap();
         assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8));
         assert_eq!(decoder.dimensions().unwrap(), (100, 100));
+
+        let mut all_tags = decoder
+            .tag_iter()
+            .filter_map(Result::ok)
+            .collect::<Vec<_>>();
+        all_tags.sort_by_key(|(t, _)| t.to_u16());
+        assert_eq!(
+            all_tags,
+            vec![
+                (Tag::ImageWidth, ifd::Value::Unsigned(100)),
+                (Tag::ImageLength, ifd::Value::Unsigned(100)),
+                (
+                    Tag::BitsPerSample,
+                    ifd::Value::List(vec![
+                        ifd::Value::UnsignedBig(8),
+                        ifd::Value::UnsignedBig(8),
+                        ifd::Value::UnsignedBig(8)
+                    ])
+                ),
+                (Tag::Compression, ifd::Value::Unsigned(1)),
+                (Tag::PhotometricInterpretation, ifd::Value::Unsigned(2)),
+                (Tag::StripOffsets, ifd::Value::Unsigned(8)),
+                (Tag::SamplesPerPixel, ifd::Value::Unsigned(3)),
+                (Tag::RowsPerStrip, ifd::Value::Unsigned(3334)),
+                (Tag::StripByteCounts, ifd::Value::Unsigned(30000)),
+                (Tag::XResolution, ifd::Value::Rational(1, 1)),
+                (Tag::YResolution, ifd::Value::Rational(1, 1)),
+                (Tag::ResolutionUnit, ifd::Value::Unsigned(1)),
+                (Tag::Artist, ifd::Value::Ascii("Image-tiff".into())),
+                (Tag::Predictor, ifd::Value::Unsigned(1)),
+                (
+                    Tag::SampleFormat,
+                    ifd::Value::List(vec![
+                        ifd::Value::UnsignedBig(1),
+                        ifd::Value::UnsignedBig(1),
+                        ifd::Value::UnsignedBig(1)
+                    ])
+                ),
+            ]
+        );
+
         assert_eq!(
             decoder.get_tag(Tag::Artist).unwrap(),
             ifd::Value::Ascii("Image-tiff".into())
@@ -75,6 +116,47 @@ fn encode_decode_big() {
         let mut decoder = Decoder::new(&mut file).unwrap();
         assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8));
         assert_eq!(decoder.dimensions().unwrap(), (100, 100));
+
+        let mut all_tags = decoder
+            .tag_iter()
+            .filter_map(Result::ok)
+            .collect::<Vec<_>>();
+        all_tags.sort_by_key(|(t, _)| t.to_u16());
+        assert_eq!(
+            all_tags,
+            vec![
+                (Tag::ImageWidth, ifd::Value::Unsigned(100)),
+                (Tag::ImageLength, ifd::Value::Unsigned(100)),
+                (
+                    Tag::BitsPerSample,
+                    ifd::Value::List(vec![
+                        ifd::Value::Short(8),
+                        ifd::Value::Short(8),
+                        ifd::Value::Short(8)
+                    ])
+                ),
+                (Tag::Compression, ifd::Value::Unsigned(1)),
+                (Tag::PhotometricInterpretation, ifd::Value::Unsigned(2)),
+                (Tag::StripOffsets, ifd::Value::UnsignedBig(16)),
+                (Tag::SamplesPerPixel, ifd::Value::Unsigned(3)),
+                (Tag::RowsPerStrip, ifd::Value::Unsigned(3334)),
+                (Tag::StripByteCounts, ifd::Value::UnsignedBig(30000)),
+                (Tag::XResolution, ifd::Value::Rational(1, 1)),
+                (Tag::YResolution, ifd::Value::Rational(1, 1)),
+                (Tag::ResolutionUnit, ifd::Value::Unsigned(1)),
+                (Tag::Artist, ifd::Value::Ascii("Image-tiff".into())),
+                (Tag::Predictor, ifd::Value::Unsigned(1)),
+                (
+                    Tag::SampleFormat,
+                    ifd::Value::List(vec![
+                        ifd::Value::Short(1),
+                        ifd::Value::Short(1),
+                        ifd::Value::Short(1)
+                    ])
+                ),
+            ]
+        );
+
         assert_eq!(
             decoder.get_tag(Tag::Artist).unwrap(),
             ifd::Value::Ascii("Image-tiff".into())