@@ -9,18 +9,81 @@ use std::sync::Arc;
9
9
10
10
/// Trait to implement for custom tags, such as Geo, EXIF, OME, etc
11
11
/// 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
+ /// ```
12
71
// Send + Sync are required for Python, where `dyn ExtraTags` needs `Send` and `Sync`
13
72
pub trait ExtraTags : ExtraTagsBlankets + Any + Debug + Send + Sync {
14
73
/// a list of tags this entry processes
74
+ ///
15
75
/// e.g. for Geo this would be [34735, 34736, 34737]
16
76
fn tags ( & self ) -> & ' static [ Tag ] ;
17
77
/// process a single tag, using internal mutability if needed
18
78
fn process_tag ( & self , tag : Tag , value : Value ) -> AsyncTiffResult < ( ) > ;
19
79
}
20
80
21
81
// 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
24
87
pub trait ExtraTagsBlankets {
25
88
fn clone_arc ( & self ) -> Arc < dyn ExtraTags > ;
26
89
fn as_any_arc ( self : Arc < Self > ) -> Arc < dyn Any + Send + Sync > ;
0 commit comments