1
+ //! # Register parsers for additional tags
2
+ //!
3
+ //! Simplified example for exif tags parser
4
+ //!
5
+ //! ```
6
+ //! # use std::sync::{LazyLock, OnceLock, Arc};
7
+ //! # use std::env::current_dir;
8
+ //! # use async_tiff::tiff::{Value, tags::Tag};
9
+ //! # use async_tiff::error::AsyncTiffResult;
10
+ //! # use async_tiff::reader::{ObjectReader, AsyncFileReader};
11
+ //! # use async_tiff::metadata::TiffMetadataReader;
12
+ //! use async_tiff::metadata::extra_tags::{ExtraTags, ExtraTagsRegistry};
13
+ //! # use object_store::local::LocalFileSystem;
14
+ //! // see https://www.media.mit.edu/pia/Research/deepview/exif.html#ExifTags
15
+ //! // or exif spec: https://www.cipa.jp/std/documents/download_e.html?DC-008-Translation-2023-E
16
+ //! // / all tags processed by your extension
17
+ //! pub const EXIF_TAGS: [Tag; 3] = [
18
+ //! Tag::Unknown(34665), // Exif IFD pointer
19
+ //! Tag::Unknown(34853), // GPS IFD pointer
20
+ //! Tag::Unknown(40965), // Interoperability IFD pointer
21
+ //! ];
22
+ //!
23
+ //! // / the struct that stores the data (using interior mutability)
24
+ //! #[derive(Debug, Clone, Default)]
25
+ //! pub struct ExifTags {
26
+ //! pub exif: OnceLock<u32>,
27
+ //! pub gps: OnceLock<u32>,
28
+ //! pub interop: OnceLock<u32>,
29
+ //! // would also hold e.g. a TiffMetadataReader to read exif IFDs
30
+ //! }
31
+ //!
32
+ //! impl ExtraTags for ExifTags {
33
+ //! fn tags(&self) -> &'static [Tag] {
34
+ //! &EXIF_TAGS
35
+ //! }
36
+ //!
37
+ //! fn process_tag(&self, tag:Tag, value: Value) -> AsyncTiffResult<()> {
38
+ //! match tag {
39
+ //! Tag::Unknown(34665) => self.exif.set(value.into_u32()?).unwrap(),
40
+ //! Tag::Unknown(34853) => self.gps.set(value.into_u32()?).unwrap(),
41
+ //! Tag::Unknown(40965) => self.interop.set(value.into_u32()?).unwrap(),
42
+ //! _ => {}
43
+ //! }
44
+ //! Ok(())
45
+ //! }
46
+ //! }
47
+ //!
48
+ //! #[tokio::main]
49
+ //! async fn main() {
50
+ //! // create an empty registry
51
+ //! let mut registry = ExtraTagsRegistry::new();
52
+ //! // register our custom extra tags
53
+ //! registry.register(Arc::new(ExifTags::default()));
54
+ //!
55
+ //! let store = Arc::new(LocalFileSystem::new_with_prefix(current_dir().unwrap()).unwrap());
56
+ //! let path = "tests/sample-exif.tiff";
57
+ //! let reader =
58
+ //! Arc::new(ObjectReader::new(store.clone(), path.into())) as Arc<dyn AsyncFileReader>;
59
+ //! let mut metadata_reader = TiffMetadataReader::try_open(&reader).await.unwrap();
60
+ //! // get the first ifd
61
+ //! let ifd = &metadata_reader
62
+ //! .read_all_ifds(&reader, registry)
63
+ //! .await
64
+ //! .unwrap()[0];
65
+ //!
66
+ //! // access by any of our registered tags
67
+ //! let exif = ifd.extra_tags()[&EXIF_TAGS[0]]
68
+ //! .clone()
69
+ //! .as_any_arc()
70
+ //! .downcast::<ExifTags>()
71
+ //! .unwrap();
72
+ //! assert!(exif.exif.get().is_some());
73
+ //! assert!(exif.gps.get().is_some());
74
+ //! // our image doesn't have interop info
75
+ //! assert!(exif.interop.get().is_none());
76
+ //! }
77
+ //! ```
78
+
1
79
use crate :: error:: { AsyncTiffError , AsyncTiffResult } ;
2
80
use crate :: tiff:: tags:: Tag ;
3
81
use crate :: tiff:: Value ;
@@ -8,21 +86,105 @@ use std::ops::Index;
8
86
use std:: sync:: Arc ;
9
87
10
88
/// Trait to implement for custom tags, such as Geo, EXIF, OME, etc
11
- /// your type should also implement `Clone`
89
+ ///
90
+ /// your type should also implement `Clone` for blanket implementations of [`ExtraTagsBlankets`]
91
+ ///
92
+ /// ```
93
+ /// # use async_tiff::tiff::{Value, tags::Tag};
94
+ /// # use async_tiff::error::AsyncTiffResult;
95
+ /// use async_tiff::metadata::extra_tags::ExtraTags;
96
+ /// # use std::sync::OnceLock;
97
+ ///
98
+ /// pub const EXIF_TAGS: [Tag; 3] = [
99
+ /// Tag::Unknown(34665), // Exif IFD pointer
100
+ /// Tag::Unknown(34853), // GPS IFD pointer
101
+ /// Tag::Unknown(40965), // Interoperability IFD pointer
102
+ /// ];
103
+ ///
104
+ /// // / the struct that stores the data (using interior mutability)
105
+ /// #[derive(Debug, Clone, Default)]
106
+ /// pub struct ExifTags {
107
+ /// pub exif: OnceLock<u32>,
108
+ /// pub gps: OnceLock<u32>,
109
+ /// pub interop: OnceLock<u32>,
110
+ /// // would also hold e.g. a TiffMetadataReader to read exif IFDs
111
+ /// }
112
+ ///
113
+ /// impl ExtraTags for ExifTags {
114
+ /// fn tags(&self) -> &'static [Tag] {
115
+ /// &EXIF_TAGS
116
+ /// }
117
+ ///
118
+ /// fn process_tag(&self, tag:Tag, value: Value) -> AsyncTiffResult<()> {
119
+ /// match tag {
120
+ /// Tag::Unknown(34665) => self.exif.set(value.into_u32()?).unwrap(),
121
+ /// Tag::Unknown(34853) => self.gps.set(value.into_u32()?).unwrap(),
122
+ /// Tag::Unknown(40965) => self.interop.set(value.into_u32()?).unwrap(),
123
+ /// _ => {}
124
+ /// }
125
+ /// Ok(())
126
+ /// }
127
+ /// }
128
+ /// ```
12
129
// Send + Sync are required for Python, where `dyn ExtraTags` needs `Send` and `Sync`
13
130
pub trait ExtraTags : ExtraTagsBlankets + Any + Debug + Send + Sync {
14
131
/// a list of tags this entry processes
132
+ ///
15
133
/// e.g. for Geo this would be [34735, 34736, 34737]
16
134
fn tags ( & self ) -> & ' static [ Tag ] ;
17
135
/// process a single tag, using internal mutability if needed
18
136
fn process_tag ( & self , tag : Tag , value : Value ) -> AsyncTiffResult < ( ) > ;
19
137
}
20
138
21
- // we need to do a little dance to do an object-safe deep clone
22
- // https://stackoverflow.com/a/30353928/14681457
23
- // also object-safe type conversions for downcasting
139
+ /// Extra trait with blanket implementations for object-safe cloning and casting
140
+ ///
141
+ /// Automatically implemented if your type implements [`ExtraTags`] and [`Clone`]
142
+ ///
143
+ /// ```
144
+ /// # use std::sync::Arc;
145
+ /// # use async_tiff::tiff::{Value, tags::Tag};
146
+ /// # use async_tiff::error::AsyncTiffResult;
147
+ /// use async_tiff::metadata::extra_tags::ExtraTags;
148
+ /// // derive these
149
+ /// #[derive(Debug, Clone)]
150
+ /// pub struct MyTags;
151
+ ///
152
+ /// // custom functionality
153
+ /// impl MyTags {
154
+ /// fn forty_two(&self) -> u32 {42}
155
+ /// }
156
+ ///
157
+ /// // implement ExtraTags
158
+ /// impl ExtraTags for MyTags {
159
+ /// fn tags(&self) -> &'static [Tag] {
160
+ /// &[]
161
+ /// }
162
+ ///
163
+ /// fn process_tag(&self, _tag:Tag, _value:Value) -> AsyncTiffResult<()> {
164
+ /// Ok(())
165
+ /// }
166
+ /// }
167
+ ///
168
+ /// fn main() {
169
+ /// // allows for deep cloning
170
+ /// let my_tags = Arc::new(MyTags) as Arc<dyn ExtraTags>;
171
+ /// let other_my_tags = my_tags.clone_arc();
172
+ /// assert!(Arc::ptr_eq(&my_tags, &my_tags.clone()));
173
+ /// assert!(!Arc::ptr_eq(&my_tags, &other_my_tags));
174
+ ///
175
+ /// // and downcasting
176
+ /// let my_tags_concrete = my_tags.as_any_arc().downcast::<MyTags>().unwrap();
177
+ /// assert_eq!(my_tags_concrete.forty_two(), 42);
178
+ /// }
179
+ /// ```
180
+ ///
181
+ /// This works since blanket implementations are done on concrete types and only
182
+ /// their signatures (function pointer) will end up in the vtable
183
+ /// <https://stackoverflow.com/a/30353928/14681457>
24
184
pub trait ExtraTagsBlankets {
185
+ /// deep clone
25
186
fn clone_arc ( & self ) -> Arc < dyn ExtraTags > ;
187
+ /// convert to any for downcasting
26
188
fn as_any_arc ( self : Arc < Self > ) -> Arc < dyn Any + Send + Sync > ;
27
189
}
28
190
@@ -40,7 +202,36 @@ where
40
202
}
41
203
42
204
/// The registry in which extra tags (parsers) are registered
43
- /// This is passed to TODO
205
+ ///
206
+ /// Pass this to [`crate::metadata::TiffMetadataReader`] when reading.
207
+ ///
208
+ /// ```
209
+ /// # use async_tiff::reader::{AsyncFileReader, ObjectReader};
210
+ /// # use async_tiff::metadata::TiffMetadataReader;
211
+ /// use async_tiff::metadata::extra_tags::ExtraTagsRegistry;
212
+ /// # use std::sync::Arc;
213
+ /// # use std::env::current_dir;
214
+ /// # use object_store::local::LocalFileSystem;
215
+ ///
216
+ /// #[tokio::main]
217
+ /// async fn main() {
218
+ /// let registry = ExtraTagsRegistry::default();
219
+ ///
220
+ /// let store = Arc::new(LocalFileSystem::new_with_prefix(current_dir().unwrap()).unwrap());
221
+ /// # let path = "tests/sample-exif.tiff";
222
+ /// let reader =
223
+ /// Arc::new(ObjectReader::new(store.clone(), path.into())) as Arc<dyn AsyncFileReader>;
224
+ /// let mut metadata_reader = TiffMetadataReader::try_open(&reader).await.unwrap();
225
+ /// // get first ifd
226
+ /// let ifd = &metadata_reader
227
+ /// .read_all_ifds(&reader, registry)
228
+ /// .await
229
+ /// .unwrap()[0];
230
+ /// // retrieve the registry
231
+ /// println!("{:?}",ifd.extra_tags());
232
+ /// }
233
+ /// ```
234
+ ///
44
235
#[ derive( Debug , Clone ) ]
45
236
pub struct ExtraTagsRegistry ( HashMap < Tag , Arc < dyn ExtraTags > > ) ;
46
237
0 commit comments