Skip to content

Commit 10cabc1

Browse files
committed
Add support for extracting Exif data
1 parent afd7ed4 commit 10cabc1

File tree

3 files changed

+44
-0
lines changed

3 files changed

+44
-0
lines changed

src/decoder.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ pub struct Decoder<R> {
7575

7676
icc_markers: Vec<IccChunk>,
7777

78+
exif_data: Option<Vec<u8>>,
79+
7880
// Used for progressive JPEGs.
7981
coefficients: Vec<Vec<i16>>,
8082
// Bitmask of which coefficients has been completely decoded.
@@ -95,6 +97,7 @@ impl<R: Read> Decoder<R> {
9597
is_jfif: false,
9698
is_mjpeg: false,
9799
icc_markers: Vec::new(),
100+
exif_data: None,
98101
coefficients: Vec::new(),
99102
coefficients_finished: [0; MAX_COMPONENTS],
100103
}
@@ -124,6 +127,13 @@ impl<R: Read> Decoder<R> {
124127
}
125128
}
126129

130+
/// Returns raw exif data, starting at the TIFF header, if the image contains any.
131+
///
132+
/// The returned value will be `None` until a call to `decode` has returned `Ok`.
133+
pub fn exif_data(&self) -> Option<Vec<u8>> {
134+
self.exif_data.as_ref().map(|v| v.clone())
135+
}
136+
127137
/// Returns the embeded icc profile if the image contains one.
128138
pub fn icc_profile(&self) -> Option<Vec<u8>> {
129139
let mut marker_present: [Option<&IccChunk>; 256] = [None; 256];
@@ -374,6 +384,7 @@ impl<R: Read> Decoder<R> {
374384
},
375385
AppData::Avi1 => self.is_mjpeg = true,
376386
AppData::Icc(icc) => self.icc_markers.push(icc),
387+
AppData::Exif(data) => self.exif_data = Some(data),
377388
}
378389
}
379390
},

src/parser.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ pub enum AppData {
7171
Jfif,
7272
Avi1,
7373
Icc(IccChunk),
74+
Exif(Vec<u8>),
7475
}
7576

7677
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe
@@ -562,6 +563,23 @@ pub fn parse_app<R: Read>(reader: &mut R, marker: Marker) -> Result<Option<AppDa
562563
}
563564
}
564565
}
566+
// Exif Data
567+
APP(1) => {
568+
if length >= 6 {
569+
let mut buffer = [0u8; 6];
570+
reader.read_exact(&mut buffer)?;
571+
bytes_read = buffer.len();
572+
573+
// https://web.archive.org/web/20190624045241if_/http://www.cipa.jp:80/std/documents/e/DC-008-Translation-2019-E.pdf
574+
// 4.5.4 Basic Structure of JPEG Compressed Data
575+
if &buffer == b"Exif\x00\x00" {
576+
let mut data = vec![0; length - bytes_read];
577+
reader.read_exact(&mut data)?;
578+
bytes_read += data.len();
579+
result = Some(AppData::Exif(data));
580+
}
581+
}
582+
}
565583
APP(2) => {
566584
if length > 14 {
567585
let mut buffer = [0u8; 14];

tests/lib.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,18 @@ fn read_icc_profile() {
4242
// "acsp" is a mandatory string in ICC profile headers.
4343
assert_eq!(&profile[36..40], b"acsp");
4444
}
45+
46+
#[test]
47+
fn read_exif_data() {
48+
let path = Path::new("tests")
49+
.join("reftest")
50+
.join("images")
51+
.join("ycck.jpg");
52+
53+
let mut decoder = jpeg::Decoder::new(File::open(&path).unwrap());
54+
decoder.decode().unwrap();
55+
56+
let exif_data = decoder.exif_data().unwrap();
57+
// exif data start as a TIFF header
58+
assert_eq!(&exif_data[0..8], b"\x49\x49\x2A\x00\x08\x00\x00\x00");
59+
}

0 commit comments

Comments
 (0)