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+
179use crate :: error:: { AsyncTiffError , AsyncTiffResult } ;
280use crate :: tiff:: tags:: Tag ;
381use crate :: tiff:: Value ;
@@ -8,21 +86,105 @@ use std::ops::Index;
886use std:: sync:: Arc ;
987
1088/// 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+ /// ```
12129// Send + Sync are required for Python, where `dyn ExtraTags` needs `Send` and `Sync`
13130pub trait ExtraTags : ExtraTagsBlankets + Any + Debug + Send + Sync {
14131 /// a list of tags this entry processes
132+ ///
15133 /// e.g. for Geo this would be [34735, 34736, 34737]
16134 fn tags ( & self ) -> & ' static [ Tag ] ;
17135 /// process a single tag, using internal mutability if needed
18136 fn process_tag ( & self , tag : Tag , value : Value ) -> AsyncTiffResult < ( ) > ;
19137}
20138
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>
24184pub trait ExtraTagsBlankets {
185+ /// deep clone
25186 fn clone_arc ( & self ) -> Arc < dyn ExtraTags > ;
187+ /// convert to any for downcasting
26188 fn as_any_arc ( self : Arc < Self > ) -> Arc < dyn Any + Send + Sync > ;
27189}
28190
@@ -40,7 +202,36 @@ where
40202}
41203
42204/// 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+ ///
44235#[ derive( Debug , Clone ) ]
45236pub struct ExtraTagsRegistry ( HashMap < Tag , Arc < dyn ExtraTags > > ) ;
46237
0 commit comments