Skip to content

Commit 7d0f5da

Browse files
committed
feat: Support BigTiff
1 parent d707d3b commit 7d0f5da

File tree

6 files changed

+45
-20
lines changed

6 files changed

+45
-20
lines changed

src/exif/exif_exif.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,15 @@ impl From<ExifIter> for Exif {
158158
#[derive(Clone, Debug, PartialEq, Eq)]
159159
pub(crate) struct TiffHeader {
160160
pub endian: Endianness,
161-
pub ifd0_offset: u32,
161+
pub bigtiff: bool,
162+
pub ifd0_offset: u64,
162163
}
163164

164165
impl Default for TiffHeader {
165166
fn default() -> Self {
166167
Self {
167168
endian: Endianness::Big,
169+
bigtiff: false,
168170
ifd0_offset: 0,
169171
}
170172
}
@@ -176,13 +178,20 @@ impl TiffHeader {
176178
pub fn parse(input: &[u8]) -> IResult<&[u8], TiffHeader> {
177179
use nom::number::streaming::{u16, u32};
178180
let (remain, endian) = TiffHeader::parse_endian(input)?;
179-
let (_, (_, offset)) = sequence::tuple((
180-
combinator::verify(u16(endian), |magic| *magic == 0x2a),
181-
u32(endian),
182-
))(remain)?;
181+
let (remain, bigtiff) = TiffHeader::parse_bigtiff(remain, endian)?;
182+
let (remain, offset) = if bigtiff {
183+
nom::number::streaming::u16(endian)(remain)?;
184+
nom::number::streaming::u16(endian)(remain)?;
185+
// offset
186+
nom::number::streaming::u64(endian)(remain)?
187+
} else {
188+
let (remain, offset) = nom::number::streaming::u32(endian)(remain)?;
189+
(remain, offset as u64)
190+
};
183191

184192
let header = Self {
185193
endian,
194+
bigtiff,
186195
ifd0_offset: offset,
187196
};
188197

@@ -227,6 +236,15 @@ impl TiffHeader {
227236
}
228237
})(input)
229238
}
239+
240+
fn parse_bigtiff(input: &[u8], endian: Endianness) -> IResult<&[u8], bool> {
241+
let (remain, magic_marker) = nom::number::streaming::u16(endian)(input)?; // Safe-slice
242+
if magic_marker == 43 {
243+
Ok((remain, true))
244+
} else {
245+
Ok((remain, false))
246+
}
247+
}
230248
}
231249

232250
pub(crate) fn check_exif_header(data: &[u8]) -> Result<bool, nom::Err<nom::error::Error<&[u8]>>> {
@@ -270,6 +288,7 @@ mod tests {
270288
header,
271289
TiffHeader {
272290
endian: Endianness::Big,
291+
bigtiff: false,
273292
ifd0_offset: 8,
274293
}
275294
);

src/exif/exif_iter.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ impl ExifIter {
182182
let mut gps_subifd = match IfdIter::try_new(
183183
gps.ifd,
184184
iter.input.partial(&iter.input[offset as usize..]), // Safe-slice
185-
offset,
185+
offset as u64,
186186
iter.tiff_header.endian,
187187
iter.tz.clone(),
188188
) {
@@ -413,7 +413,7 @@ impl Iterator for ExifIter {
413413
return Some(ParsedExifEntry::make_ok(
414414
ifd_idx,
415415
tag_code.unwrap(),
416-
EntryValue::U32(offset),
416+
EntryValue::U64(offset),
417417
));
418418
}
419419
}
@@ -446,7 +446,7 @@ pub(crate) struct IfdIter {
446446
input: AssociatedInput,
447447

448448
// IFD data offset relative to the TIFF header.
449-
offset: u32,
449+
offset: u64,
450450

451451
pub tz: Option<String>,
452452
endian: Endianness,
@@ -506,7 +506,7 @@ impl IfdIter {
506506
pub fn try_new(
507507
ifd_idx: usize,
508508
input: AssociatedInput,
509-
offset: u32,
509+
offset: u64,
510510
endian: Endianness,
511511
tz: Option<String>,
512512
) -> crate::Result<Self> {
@@ -541,6 +541,7 @@ impl IfdIter {
541541
complete::u32(endian),
542542
))(entry_data)
543543
.ok()?;
544+
let value_or_offset = value_or_offset as u64;
544545

545546
if tag == 0 {
546547
return None;
@@ -558,7 +559,7 @@ impl IfdIter {
558559
Some((tag, res))
559560
}
560561

561-
fn get_data_pos(&self, value_or_offset: u32) -> u32 {
562+
fn get_data_pos(&self, value_or_offset: u64) -> u64 {
562563
value_or_offset.saturating_sub(self.offset)
563564
}
564565

@@ -568,7 +569,7 @@ impl IfdIter {
568569
data_format: DataFormat,
569570
components_num: u32,
570571
entry_data: &[u8],
571-
value_or_offset: u32,
572+
value_or_offset: u64,
572573
) -> (u16, IfdEntry) {
573574
// get component_size according to data format
574575
let component_size = data_format.component_size();
@@ -617,7 +618,7 @@ impl IfdIter {
617618
fn new_ifd_iter(
618619
&self,
619620
ifd_idx: usize,
620-
value_or_offset: u32,
621+
value_or_offset: u64,
621622
tag: Option<u16>,
622623
) -> Option<IfdEntry> {
623624
let pos = self.get_data_pos(value_or_offset) as usize;
@@ -862,6 +863,7 @@ impl Iterator for IfdIter {
862863
let (_, offset) =
863864
complete::u32::<_, nom::error::Error<_>>(endian)(&self.input[self.pos..]).ok()?;
864865

866+
let offset = offset as u64;
865867
if offset == 0 {
866868
// IFD parsing completed
867869
tracing::debug!(?self, "IFD parsing completed");
@@ -899,6 +901,7 @@ mod tests {
899901
#[test_case("broken.jpg", "", MimeImage::Jpeg)]
900902
#[test_case("exif.heic", "+08:00", MimeImage::Heic)]
901903
#[test_case("tif.tif", "", MimeImage::Tiff)]
904+
//#[test_case("bif.tif", "", MimeImage::Tiff)]
902905
#[test_case("fujifilm_x_t1_01.raf.meta", "", MimeImage::Raf)]
903906
fn exif_iter_tz(path: &str, tz: &str, img_type: MimeImage) {
904907
let buf = read_sample(path).unwrap();

src/exif/travel.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub(crate) struct IfdHeaderTravel<'a> {
1616
ifd_data: &'a [u8],
1717

1818
// IFD data offset relative to the TIFF header.
19-
offset: u32,
19+
offset: u64,
2020

2121
endian: Endianness,
2222
}
@@ -30,11 +30,11 @@ pub(crate) struct EntryInfo<'a> {
3030
pub data_format: DataFormat,
3131
#[allow(unused)]
3232
pub data_offset: Option<u32>,
33-
pub sub_ifd_offset: Option<u32>,
33+
pub sub_ifd_offset: Option<u64>,
3434
}
3535

3636
impl<'a> IfdHeaderTravel<'a> {
37-
pub fn new(input: &'a [u8], offset: u32, endian: Endianness) -> Self {
37+
pub fn new(input: &'a [u8], offset: u64, endian: Endianness) -> Self {
3838
Self {
3939
ifd_data: input,
4040
endian,
@@ -54,6 +54,7 @@ impl<'a> IfdHeaderTravel<'a> {
5454
streaming::u32(endian),
5555
streaming::u32(endian),
5656
))(entry_data)?;
57+
let value_or_offset = value_or_offset as u64;
5758

5859
if tag == 0 {
5960
return Ok((remain, None));
@@ -112,11 +113,11 @@ impl<'a> IfdHeaderTravel<'a> {
112113
Ok((&[][..], Some(entry)))
113114
}
114115

115-
fn get_data_pos(&'a self, value_or_offset: u32) -> u32 {
116+
fn get_data_pos(&'a self, value_or_offset: u64) -> u64 {
116117
value_or_offset.saturating_sub(self.offset)
117118
}
118119

119-
fn parse_ifd_entry_header(&self, pos: u32) -> IResult<&[u8], Option<IfdHeaderTravel<'a>>> {
120+
fn parse_ifd_entry_header(&self, pos: u64) -> IResult<&[u8], Option<IfdHeaderTravel<'a>>> {
120121
let (_, entry_data) =
121122
nom::bytes::streaming::take(IFD_ENTRY_SIZE)(&self.ifd_data[pos as usize..])?;
122123

@@ -149,14 +150,14 @@ impl<'a> IfdHeaderTravel<'a> {
149150

150151
tracing::debug!(ifd_data_len = self.ifd_data.len(), offset = self.offset);
151152
let (remain, entry_num) = TiffHeader::parse_ifd_entry_num(self.ifd_data, self.endian)?;
152-
let mut pos = self.ifd_data.len() - remain.len();
153+
let mut pos = (self.ifd_data.len() - remain.len()) as u64;
153154

154155
let mut sub_ifds = Vec::new();
155156

156157
// parse entries
157158
for _ in 0..entry_num {
158-
let (_, sub_ifd) = self.parse_ifd_entry_header(pos as u32)?;
159-
pos += IFD_ENTRY_SIZE;
159+
let (_, sub_ifd) = self.parse_ifd_entry_header(pos)?;
160+
pos += IFD_ENTRY_SIZE as u64;
160161

161162
if let Some(ifd) = sub_ifd {
162163
if ifd.offset <= self.offset {

src/parser.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ mod tests {
525525
#[case("exif-one-entry.heic", Exif)]
526526
#[case("no-exif.jpg", NoData)]
527527
#[case("tif.tif", Exif)]
528+
//#[case("bif.tif", Exif)]
528529
#[case("ramdisk.img", Invalid)]
529530
#[case("webm_480.webm", Track)]
530531
fn parse_media(path: &str, te: TrackExif) {

src/parser_async.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,7 @@ mod tests {
485485
#[case("exif-one-entry.heic", Exif)]
486486
#[case("no-exif.jpg", NoData)]
487487
#[case("tif.tif", Exif)]
488+
//#[case("bif.tif", Exif)]
488489
#[case("ramdisk.img", Invalid)]
489490
#[case("webm_480.webm", Track)]
490491
async fn parse_media(path: &str, te: TrackExif) {

testdata/bif.tif

387 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)