@@ -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,85 @@ 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. load_and_parse :: < R , S , _ , _ > ( reader, |buf, _state| {
98+ cr3:: extract_all_cmt_ranges ( buf)
99+ } ) ?;
95100
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.
101+ let Some ( cmt_ranges) = cmt_ranges else {
102+ return Err ( "CR3: No CMT data found" . into ( ) ) ;
103+ } ;
100104
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 ;
105+ if cmt_ranges. ranges . is_empty ( ) {
106+ return Err ( "CR3: No CMT ranges available" . into ( ) ) ;
107+ }
108+
109+ tracing:: debug!(
110+ cmt_count = cmt_ranges. ranges. len( ) ,
111+ "Found CMT ranges in CR3 file"
112+ ) ;
113+
114+ // Get the parser position offset - share_buf will add this to ranges
115+ let position_offset = parser. position ( ) ;
116+
117+ // Get the first CMT range (CMT1) to create the primary ExifIter
118+ let ( first_block_id, first_range) = & cmt_ranges. ranges [ 0 ] ;
119+ tracing:: debug!(
120+ block_id = first_block_id,
121+ range = ?first_range,
122+ position_offset,
123+ "Creating primary ExifIter from first CMT block"
124+ ) ;
125+
126+ // Share the buffer and create the primary ExifIter
127+ // Note: share_buf adds position_offset to the range internally
128+ let input: PartialVec = parser. share_buf ( first_range. clone ( ) ) ;
129+ let mut iter = input_into_iter ( input, None ) ?;
130+
131+ // Add remaining CMT blocks as additional TIFF blocks
132+ // We need to adjust the ranges by position_offset since the PartialVec.data
133+ // contains the full buffer and ranges need to be absolute
134+ // Note: We skip CMT3 (MakerNotes) as it has a proprietary format that requires
135+ // special handling and would produce garbage data if parsed as standard EXIF
136+ for ( block_id, range) in cmt_ranges. ranges . iter ( ) . skip ( 1 ) {
137+ // Skip CMT3 (MakerNotes) - it has a proprietary Canon format
138+ if * block_id == "CMT3" {
139+ tracing:: debug!(
140+ block_id,
141+ "Skipping CMT3 (MakerNotes) - proprietary format"
142+ ) ;
143+ continue ;
107144 }
108145
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 ) ;
146+ let adjusted_range = ( range. start + position_offset) ..( range. end + position_offset) ;
147+ tracing:: debug!(
148+ block_id,
149+ original_range = ?range,
150+ adjusted_range = ?adjusted_range,
151+ "Adding additional CMT block"
152+ ) ;
153+ iter. add_tiff_block ( block_id. to_string ( ) , adjusted_range, None ) ;
115154 }
116155
117156 Ok ( iter)
0 commit comments