@@ -227,6 +227,8 @@ pub enum Status {
227227 IlocOffsetOverflow ,
228228 ImageItemType ,
229229 InfeFlagsNonzero ,
230+ InfeStringNoNul ,
231+ InfeStringNotUtf8 ,
230232 InvalidUtf8 ,
231233 IpcoIndexOverflow ,
232234 IpmaBadIndex ,
@@ -611,6 +613,14 @@ impl From<Status> for &str {
611613 "'infe' flags field shall be 0 \
612614 per ISOBMFF (ISO 14496-12:2020) § 8.11.6.2"
613615 }
616+ Status :: InfeStringNoNul => {
617+ "'infe' strings shall be null-terminated \
618+ per ISOBMFF (ISO 14496-12:2020) § 8.11.6.3"
619+ }
620+ Status :: InfeStringNotUtf8 => {
621+ "'infe' strings field shall be valid utf8 \
622+ per ISOBMFF (ISO 14496-12:2020) § 8.11.6.3"
623+ }
614624 Status :: InvalidUtf8 => {
615625 "invalid utf8"
616626 }
@@ -1594,6 +1604,10 @@ pub struct AvifContext {
15941604 pub sequence : Option < MediaContext > ,
15951605 /// A collection of unsupported features encountered during the parse
15961606 pub unsupported_features : UnsupportedFeatures ,
1607+ /// AVIF box containing the Exif metadata, if any
1608+ exif_metadata : Option < AvifItem > ,
1609+ /// AVIF box containing the XMP metadata, if any
1610+ xmp_metadata : Option < AvifItem > ,
15971611}
15981612
15991613impl AvifContext {
@@ -1656,6 +1670,18 @@ impl AvifContext {
16561670 }
16571671
16581672
1673+ pub fn exif_metadata ( & self ) -> Option < & [ u8 ] > {
1674+ self . exif_metadata
1675+ . as_ref ( )
1676+ . map ( |item| self . item_as_slice ( item) )
1677+ }
1678+
1679+ pub fn xmp_metadata ( & self ) -> Option < & [ u8 ] > {
1680+ self . xmp_metadata
1681+ . as_ref ( )
1682+ . map ( |item| self . item_as_slice ( item) )
1683+ }
1684+
16591685 pub fn spatial_extents ( & self ) -> Result < & ImageSpatialExtentsProperty > {
16601686 if let Some ( primary_item) = & self . primary_item {
16611687 match self
@@ -1970,7 +1996,7 @@ impl DataBox {
19701996#[ derive( Clone , Copy , Debug , Eq , Hash , PartialEq ) ]
19711997struct PropertyIndex ( u16 ) ;
19721998#[ derive( Clone , Copy , Debug , Eq , Hash , PartialEq , PartialOrd ) ]
1973- struct ItemId ( u32 ) ;
1999+ pub struct ItemId ( u32 ) ;
19742000
19752001impl ItemId {
19762002 fn read ( src : & mut impl ReadBytesExt , version : u8 ) -> Result < ItemId > {
@@ -1986,9 +2012,13 @@ impl ItemId {
19862012/// See ISOBMFF (ISO 14496-12:2020) § 8.11.6
19872013/// Only versions {2, 3} are supported
19882014#[ derive( Debug ) ]
1989- struct ItemInfoEntry {
1990- item_id : ItemId ,
1991- item_type : u32 ,
2015+ pub struct ItemInfoEntry {
2016+ pub item_id : ItemId ,
2017+ pub item_type : u32 ,
2018+ pub item_name : TryVec < u8 > ,
2019+ pub content_type : Option < TryVec < u8 > > ,
2020+ pub content_encoding : Option < TryVec < u8 > > ,
2021+ pub uri_type : Option < TryVec < u8 > > ,
19922022}
19932023
19942024/// See ISOBMFF (ISO 14496-12:2020) § 8.11.12
@@ -2577,24 +2607,17 @@ pub fn read_avif<T: Read>(f: &mut T, strictness: ParseStrictness) -> Result<Avif
25772607 debug ! ( "alpha_item_id: {alpha_item_id:?}" ) ;
25782608 let mut primary_item = None ;
25792609 let mut alpha_item = None ;
2610+ let mut items: TryHashMap < ItemId , AvifItem > = Default :: default ( ) ;
25802611
25812612 // store data or record location of relevant items
25822613 for ( item_id, loc) in iloc_items {
2583- let item = if Some ( item_id) == primary_item_id {
2584- & mut primary_item
2585- } else if Some ( item_id) == alpha_item_id {
2586- & mut alpha_item
2587- } else {
2588- continue ;
2589- } ;
2590-
2591- assert ! ( item. is_none( ) ) ;
2614+ let mut item: Option < AvifItem > = None ;
25922615
25932616 // If our item is spread over multiple extents, we'll need to copy it
25942617 // into a contiguous buffer. Otherwise, we can just store the extent
25952618 // and return a pointer into the mdat/idat later to avoid the copy.
25962619 if loc. extents . len ( ) > 1 {
2597- * item = Some ( AvifItem :: with_inline_data ( item_id) )
2620+ item = Some ( AvifItem :: with_inline_data ( item_id) )
25982621 }
25992622
26002623 trace ! (
@@ -2607,10 +2630,10 @@ pub fn read_avif<T: Read>(f: &mut T, strictness: ParseStrictness) -> Result<Avif
26072630 // true if the extent is successfully added to the AvifItem
26082631 let mut find_and_add_to_item = |extent : & Extent , dat : & DataBox | -> Result < bool > {
26092632 if let Some ( extent_slice) = dat. get ( extent) {
2610- match item {
2633+ match & mut item {
26112634 None => {
26122635 trace ! ( "Using IsobmffItem::Location" ) ;
2613- * item = Some ( AvifItem {
2636+ item = Some ( AvifItem {
26142637 id : item_id,
26152638 image_data : dat. location ( extent) ,
26162639 } ) ;
@@ -2670,6 +2693,14 @@ pub fn read_avif<T: Read>(f: &mut T, strictness: ParseStrictness) -> Result<Avif
26702693 }
26712694
26722695 assert ! ( item. is_some( ) ) ;
2696+
2697+ if Some ( item_id) == primary_item_id {
2698+ primary_item = item;
2699+ } else if Some ( item_id) == alpha_item_id {
2700+ alpha_item = item;
2701+ } else {
2702+ items. insert ( item_id, item. unwrap ( ) ) ?;
2703+ } ;
26732704 }
26742705
26752706 if ( primary_item_id. is_some ( ) && primary_item. is_none ( ) )
@@ -2773,6 +2804,21 @@ pub fn read_avif<T: Read>(f: &mut T, strictness: ParseStrictness) -> Result<Avif
27732804 check_image_item ( & mut primary_item) ?;
27742805 check_image_item ( & mut alpha_item) ?;
27752806
2807+ let mut exif_metadata = None ;
2808+ let mut xmp_metadata = None ;
2809+
2810+ item_infos. into_iter ( ) . for_each ( |item| {
2811+ if & item. item_type . to_be_bytes ( ) == b"Exif" {
2812+ exif_metadata = items. remove ( & item. item_id ) ;
2813+ } else if & item. item_type . to_be_bytes ( ) == b"mime"
2814+ && item
2815+ . content_type
2816+ . is_some_and ( |v| v. as_slice ( ) == b"application/rdf+xml" )
2817+ {
2818+ xmp_metadata = items. remove ( & item. item_id ) ;
2819+ }
2820+ } ) ;
2821+
27762822 Ok ( AvifContext {
27772823 strictness,
27782824 media_storage,
@@ -2783,6 +2829,8 @@ pub fn read_avif<T: Read>(f: &mut T, strictness: ParseStrictness) -> Result<Avif
27832829 item_properties,
27842830 major_brand,
27852831 sequence : image_sequence,
2832+ exif_metadata,
2833+ xmp_metadata,
27862834 unsupported_features,
27872835 } )
27882836}
@@ -2957,6 +3005,39 @@ impl std::fmt::Display for U32BE {
29573005 }
29583006}
29593007
3008+ fn read_infe_string < T : Read > (
3009+ src : & mut BMFFBox < T > ,
3010+ strictness : ParseStrictness ,
3011+ ) -> Result < TryVec < u8 > > {
3012+ let mut s = TryVec :: new ( ) ;
3013+ loop {
3014+ match src. read_u8 ( ) {
3015+ Ok ( v) => {
3016+ s. push ( v) ?;
3017+ if v == 0 {
3018+ break ;
3019+ }
3020+ } ,
3021+ Err ( _) => break ,
3022+ }
3023+ }
3024+ match std:: str:: from_utf8 ( & s) {
3025+ Ok ( s) => {
3026+ if !s. bytes ( ) . any ( |b| b == b'\0' ) {
3027+ fail_with_status_if (
3028+ strictness != ParseStrictness :: Permissive ,
3029+ Status :: InfeStringNoNul ,
3030+ ) ?;
3031+ }
3032+ }
3033+ Err ( _) => fail_with_status_if (
3034+ strictness != ParseStrictness :: Permissive ,
3035+ Status :: InfeStringNotUtf8 ,
3036+ ) ?,
3037+ }
3038+ Ok ( s)
3039+ }
3040+
29603041/// Parse an Item Info Entry
29613042/// See ISOBMFF (ISO 14496-12:2020) § 8.11.6.2
29623043fn read_infe < T : Read > (
@@ -2988,15 +3069,32 @@ fn read_infe<T: Read>(
29883069 let item_type = be_u32 ( src) ?;
29893070 debug ! ( "infe {:?} item_type: {}" , item_id, U32BE ( item_type) ) ;
29903071
2991- // There are some additional fields here, but they're not of interest to us
2992- skip_box_remain ( src) ?;
2993-
29943072 if item_protection_index != 0 {
29953073 unsupported_features. insert ( Feature :: Ipro ) ;
2996- Ok ( None )
2997- } else {
2998- Ok ( Some ( ItemInfoEntry { item_id, item_type } ) )
3074+ skip_box_remain ( src) ?;
3075+ return Ok ( None ) ;
29993076 }
3077+
3078+ let item_name = read_infe_string ( src, strictness) ?;
3079+
3080+ let ( content_type, content_encoding, uri_type) = if & item_type. to_be_bytes ( ) == b"mime" {
3081+ (
3082+ Some ( read_infe_string ( src, strictness) ?) ,
3083+ Some ( read_infe_string ( src, strictness) ?) ,
3084+ None
3085+ )
3086+ } else {
3087+ ( None , None , Some ( read_infe_string ( src, strictness) ?) )
3088+ } ;
3089+
3090+ Ok ( Some ( ItemInfoEntry {
3091+ item_id,
3092+ item_type,
3093+ item_name,
3094+ content_type,
3095+ content_encoding,
3096+ uri_type
3097+ } ) )
30003098}
30013099
30023100/// Parse an Item Reference Box
0 commit comments