@@ -12,7 +12,6 @@ use exif_exif::TIFF_HEADER_LEN;
1212use exif_iter:: input_into_iter;
1313pub use exif_iter:: { ExifIter , ParsedExifEntry } ;
1414pub use gps:: { GPSInfo , LatLng } ;
15- pub use multi_exif:: { DuplicateStrategy , MultiExifIter } ;
1615pub use tags:: ExifTag ;
1716
1817use std:: io:: Read ;
@@ -25,7 +24,6 @@ pub(crate) use travel::IfdHeaderTravel;
2524mod exif_exif;
2625mod exif_iter;
2726mod gps;
28- mod multi_exif;
2927mod tags;
3028mod travel;
3129
@@ -74,44 +72,81 @@ pub(crate) fn parse_exif_iter<R: Read, S: Skip<R>>(
7472 mime_img : MimeImage ,
7573 reader : & mut R ,
7674) -> Result < ExifIter , crate :: Error > {
75+ // For CR3 files, we need special handling to get all CMT blocks
76+ if mime_img == MimeImage :: Cr3 {
77+ return parse_cr3_exif_iter :: < R , S > ( parser, reader) ;
78+ }
79+
7780 let out = parser. load_and_parse :: < R , S , _ , _ > ( reader, |buf, state| {
7881 extract_exif_range ( mime_img, buf, state)
7982 } ) ?;
8083
8184 range_to_iter ( parser, out)
8285}
8386
87+ /// Special parser for CR3 files that extracts all CMT blocks (CMT1, CMT2, CMT3)
88+ /// and adds them as additional TIFF blocks to the ExifIter.
8489#[ tracing:: instrument( skip( reader) ) ]
85- pub ( crate ) fn parse_multi_exif_iter < R : Read , S : Skip < R > > (
90+ fn parse_cr3_exif_iter < R : Read , S : Skip < R > > (
8691 parser : & mut MediaParser ,
87- mime_img : MimeImage ,
8892 reader : & mut R ,
89- ) -> Result < MultiExifIter , crate :: Error > {
90- if mime_img != MimeImage :: Cr3 {
91- return Err ( format ! ( "MultiExifIter is not supported for {mime_img:?}" ) . into ( ) ) ;
92- }
93+ ) -> Result < ExifIter , crate :: Error > {
94+ use crate :: parser:: Buf ;
9395
94- let mut iter = MultiExifIter :: new ( DuplicateStrategy :: IgnoreDuplicates ) ;
96+ // First, parse to get all CMT ranges
97+ let cmt_ranges = parser
98+ . load_and_parse :: < R , S , _ , _ > ( reader, |buf, _state| cr3:: extract_all_cmt_ranges ( buf) ) ?;
9599
96- // TODO: The following is only demonstration code.
97- // Please make further modifications based on the CR3 file structure.
98- // For example, the `parse` callback of `load_and_parse` should be reimplemented
99- // to correctly parse the next CMT* box.
100+ let Some ( cmt_ranges) = cmt_ranges else {
101+ return Err ( "CR3: No CMT data found" . into ( ) ) ;
102+ } ;
100103
101- loop {
102- let out = parser. load_and_parse :: < R , S , _ , _ > ( reader, |buf, state| {
103- extract_exif_range ( mime_img, buf, state)
104- } ) ?;
105- if out. is_none ( ) {
106- break ;
104+ if cmt_ranges. ranges . is_empty ( ) {
105+ return Err ( "CR3: No CMT ranges available" . into ( ) ) ;
106+ }
107+
108+ tracing:: debug!(
109+ cmt_count = cmt_ranges. ranges. len( ) ,
110+ "Found CMT ranges in CR3 file"
111+ ) ;
112+
113+ // Get the parser position offset - share_buf will add this to ranges
114+ let position_offset = parser. position ( ) ;
115+
116+ // Get the first CMT range (CMT1) to create the primary ExifIter
117+ let ( first_block_id, first_range) = & cmt_ranges. ranges [ 0 ] ;
118+ tracing:: debug!(
119+ block_id = first_block_id,
120+ range = ?first_range,
121+ position_offset,
122+ "Creating primary ExifIter from first CMT block"
123+ ) ;
124+
125+ // Share the buffer and create the primary ExifIter
126+ // Note: share_buf adds position_offset to the range internally
127+ let input: PartialVec = parser. share_buf ( first_range. clone ( ) ) ;
128+ let mut iter = input_into_iter ( input, None ) ?;
129+
130+ // Add remaining CMT blocks as additional TIFF blocks
131+ // We need to adjust the ranges by position_offset since the PartialVec.data
132+ // contains the full buffer and ranges need to be absolute
133+ // Note: We skip CMT3 (MakerNotes) as it has a proprietary format that requires
134+ // special handling and would produce garbage data if parsed as standard EXIF
135+ for ( block_id, range) in cmt_ranges. ranges . iter ( ) . skip ( 1 ) {
136+ // Skip CMT3 (MakerNotes) - it has a proprietary Canon format
137+ if * block_id == "CMT3" {
138+ tracing:: debug!( block_id, "Skipping CMT3 (MakerNotes) - proprietary format" ) ;
139+ continue ;
107140 }
108141
109- // TODO: The current `block_id` should be returned via the `load_and_parse` call.
110- let block_id = "CMT1" ;
111- let data = out
112- . map ( |( range, _) | parser. share_buf ( range) )
113- . ok_or_else ( || format ! ( "Exif not found in block {block_id}" ) ) ?;
114- iter. add_tiff_data ( block_id. to_owned ( ) , data, None ) ;
142+ let adjusted_range = ( range. start + position_offset) ..( range. end + position_offset) ;
143+ tracing:: debug!(
144+ block_id,
145+ original_range = ?range,
146+ adjusted_range = ?adjusted_range,
147+ "Adding additional CMT block"
148+ ) ;
149+ iter. add_tiff_block ( block_id. to_string ( ) , adjusted_range, None ) ;
115150 }
116151
117152 Ok ( iter)
0 commit comments