@@ -9,18 +9,81 @@ use std::sync::Arc;
99
1010/// Trait to implement for custom tags, such as Geo, EXIF, OME, etc
1111/// your type should also implement `Clone`
12+ ///
13+ /// ```
14+ /// # use std::sync::{LazyLock, OnceLock, Arc};
15+ /// # use std::env::current_dir;
16+ /// # use async_tiff::tiff::{Value, tags::Tag};
17+ /// # use async_tiff::error::AsyncTiffResult;
18+ /// # use async_tiff::reader::{ObjectReader, AsyncFileReader};
19+ /// use async_tiff::metadata::{ExtraTags, ExtraTagsRegistry, TiffMetadataReader};
20+ /// # use object_store::local::LocalFileSystem;
21+ /// // see https://www.media.mit.edu/pia/Research/deepview/exif.html#ExifTags
22+ /// // or exif spec: https://www.cipa.jp/std/documents/download_e.html?DC-008-Translation-2023-E
23+ /// // / all tags processed by your extension
24+ /// pub static EXIF_TAGS: LazyLock<Vec<Tag>> = LazyLock::new(|| vec![
25+ /// Tag::Unknown(34665), // Exif IFD pointer
26+ /// Tag::Unknown(34853), // GPS IFD pointer
27+ /// Tag::Unknown(40965), // Interoperability IFD pointer
28+ /// ]);
29+ ///
30+ /// // / the struct that stores the data (using interior mutability)
31+ /// #[derive(Debug, Clone, Default)]
32+ /// pub struct ExifTags {
33+ /// pub exif: OnceLock<u32>,
34+ /// pub gps: OnceLock<u32>,
35+ /// pub interop: OnceLock<u32>,
36+ /// }
37+ ///
38+ /// impl ExtraTags for ExifTags {
39+ /// fn tags(&self) -> &'static [Tag] {
40+ /// &EXIF_TAGS
41+ /// }
42+ ///
43+ /// fn process_tag(&self, tag:Tag, value: Value) -> AsyncTiffResult<()> {
44+ /// match tag {
45+ /// Tag::Unknown(34665) => self.exif.set(value.into_u32()?).unwrap(),
46+ /// Tag::Unknown(34853) => self.gps.set(value.into_u32()?).unwrap(),
47+ /// Tag::Unknown(40965) => self.interop.set(value.into_u32()?).unwrap(),
48+ /// _ => {}
49+ /// }
50+ /// Ok(())
51+ /// }
52+ /// }
53+ ///
54+ /// #[tokio::main]
55+ /// async fn main() {
56+ /// let mut registry = ExtraTagsRegistry::new();
57+ /// registry.register(Arc::new(ExifTags::default()));
58+ /// # let store = Arc::new(LocalFileSystem::new_with_prefix(current_dir().unwrap()).unwrap());
59+ /// # let path = "tests/sample-exif.tiff";
60+ /// let reader = Arc::new(ObjectReader::new(store.clone(), path.into())) as Arc<dyn AsyncFileReader>;
61+ /// let mut metadata_reader = TiffMetadataReader::try_open(&reader).await.unwrap();
62+ /// let ifd = metadata_reader.read_next_ifd(&reader, registry).await.unwrap().unwrap();
63+ /// // access by any of our registered tags
64+ /// let exif = ifd.extra_tags()[&EXIF_TAGS[0]].clone().as_any_arc().downcast::<ExifTags>().unwrap();
65+ /// assert!(exif.exif.get().is_some());
66+ /// assert!(exif.gps.get().is_some());
67+ /// // our image doesn't have interop info
68+ /// assert!(exif.interop.get().is_none());
69+ /// }
70+ /// ```
1271// Send + Sync are required for Python, where `dyn ExtraTags` needs `Send` and `Sync`
1372pub trait ExtraTags : ExtraTagsBlankets + Any + Debug + Send + Sync {
1473 /// a list of tags this entry processes
74+ ///
1575 /// e.g. for Geo this would be [34735, 34736, 34737]
1676 fn tags ( & self ) -> & ' static [ Tag ] ;
1777 /// process a single tag, using internal mutability if needed
1878 fn process_tag ( & self , tag : Tag , value : Value ) -> AsyncTiffResult < ( ) > ;
1979}
2080
2181// 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
82+ // https://stackoverflow.com/a/30353928/14681457 also object-safe type
83+ // conversions for downcasting.
84+ //
85+ // This works since blanket implementations are done on concrete types and only
86+ // their signatures (function pointer) will end up in the vtable
2487pub trait ExtraTagsBlankets {
2588 fn clone_arc ( & self ) -> Arc < dyn ExtraTags > ;
2689 fn as_any_arc ( self : Arc < Self > ) -> Arc < dyn Any + Send + Sync > ;
0 commit comments