Skip to content

Commit c17977d

Browse files
authored
Merge pull request #268 from hfiguiere/xmp
Read the XMP packet
2 parents 269cd7d + 763ae22 commit c17977d

File tree

4 files changed

+59
-14
lines changed

4 files changed

+59
-14
lines changed

examples/decode.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ fn main() {
2323

2424
eprintln!("{:?}", info);
2525
eprintln!("Exif: {}", decoder.exif_data().is_some());
26+
eprintln!("XMP: {}", decoder.xmp_data().is_some());
2627
eprintln!("ICC: {}", decoder.icc_profile().is_some());
2728

2829
let output_file = File::create(output_path).unwrap();

src/decoder.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ pub struct Decoder<R> {
118118
icc_markers: Vec<IccChunk>,
119119

120120
exif_data: Option<Vec<u8>>,
121+
xmp_data: Option<Vec<u8>>,
122+
psir_data: Option<Vec<u8>>,
121123

122124
// Used for progressive JPEGs.
123125
coefficients: Vec<Vec<i16>>,
@@ -144,6 +146,8 @@ impl<R: Read> Decoder<R> {
144146
is_mjpeg: false,
145147
icc_markers: Vec::new(),
146148
exif_data: None,
149+
xmp_data: None,
150+
psir_data: None,
147151
coefficients: Vec::new(),
148152
coefficients_finished: [0; MAX_COMPONENTS],
149153
decoding_buffer_size_limit: usize::MAX,
@@ -197,6 +201,13 @@ impl<R: Read> Decoder<R> {
197201
self.exif_data.as_deref()
198202
}
199203

204+
/// Returns the raw XMP packet if there is any.
205+
///
206+
/// The returned value will be `None` until a call to `decode` has returned `Ok`.
207+
pub fn xmp_data(&self) -> Option<&[u8]> {
208+
self.xmp_data.as_deref()
209+
}
210+
200211
/// Returns the embeded icc profile if the image contains one.
201212
pub fn icc_profile(&self) -> Option<Vec<u8>> {
202213
let mut marker_present: [Option<&IccChunk>; 256] = [None; 256];
@@ -542,6 +553,8 @@ impl<R: Read> Decoder<R> {
542553
AppData::Avi1 => self.is_mjpeg = true,
543554
AppData::Icc(icc) => self.icc_markers.push(icc),
544555
AppData::Exif(data) => self.exif_data = Some(data),
556+
AppData::Xmp(data) => self.xmp_data = Some(data),
557+
AppData::Psir(data) => self.psir_data = Some(data),
545558
}
546559
}
547560
}

src/parser.rs

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ pub enum AppData {
9595
Avi1,
9696
Icc(IccChunk),
9797
Exif(Vec<u8>),
98+
Xmp(Vec<u8>),
99+
Psir(Vec<u8>),
98100
}
99101

100102
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe
@@ -623,21 +625,20 @@ pub fn parse_app<R: Read>(reader: &mut R, marker: Marker) -> Result<Option<AppDa
623625
}
624626
}
625627
}
626-
// Exif Data
627628
APP(1) => {
628-
if length >= 6 {
629-
let mut buffer = [0u8; 6];
630-
reader.read_exact(&mut buffer)?;
631-
bytes_read = buffer.len();
632-
633-
// https://web.archive.org/web/20190624045241if_/http://www.cipa.jp:80/std/documents/e/DC-008-Translation-2019-E.pdf
634-
// 4.5.4 Basic Structure of JPEG Compressed Data
635-
if buffer == *b"Exif\x00\x00" {
636-
let mut data = vec![0; length - bytes_read];
637-
reader.read_exact(&mut data)?;
638-
bytes_read += data.len();
639-
result = Some(AppData::Exif(data));
640-
}
629+
let mut buffer = vec![0u8; length];
630+
reader.read_exact(&mut buffer)?;
631+
bytes_read = buffer.len();
632+
633+
// https://web.archive.org/web/20190624045241if_/http://www.cipa.jp:80/std/documents/e/DC-008-Translation-2019-E.pdf
634+
// 4.5.4 Basic Structure of JPEG Compressed Data
635+
if length >= 6 && buffer[0..6] == *b"Exif\x00\x00" {
636+
result = Some(AppData::Exif(buffer[6..].to_vec()));
637+
}
638+
// XMP packet
639+
// https://github.com/adobe/XMP-Toolkit-SDK/blob/main/docs/XMPSpecificationPart3.pdf
640+
else if length >= 29 && buffer[0..29] == *b"http://ns.adobe.com/xap/1.0/\0" {
641+
result = Some(AppData::Xmp(buffer[29..].to_vec()));
641642
}
642643
}
643644
APP(2) => {
@@ -660,6 +661,22 @@ pub fn parse_app<R: Read>(reader: &mut R, marker: Marker) -> Result<Option<AppDa
660661
}
661662
}
662663
}
664+
APP(13) => {
665+
if length >= 14 {
666+
let mut buffer = [0u8; 14];
667+
reader.read_exact(&mut buffer)?;
668+
bytes_read = buffer.len();
669+
670+
// PSIR (Photoshop)
671+
// https://github.com/adobe/XMP-Toolkit-SDK/blob/main/docs/XMPSpecificationPart3.pdf
672+
if buffer[0..14] == *b"Photoshop 3.0\0" {
673+
let mut data = vec![0; length - bytes_read];
674+
reader.read_exact(&mut data)?;
675+
bytes_read += data.len();
676+
result = Some(AppData::Psir(data));
677+
}
678+
}
679+
}
663680
APP(14) => {
664681
if length >= 12 {
665682
let mut buffer = [0u8; 12];

tests/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,17 @@ fn read_exif_data() {
154154
// exif data start as a TIFF header
155155
assert_eq!(&exif_data[0..8], b"\x49\x49\x2A\x00\x08\x00\x00\x00");
156156
}
157+
158+
#[test]
159+
fn read_xmp_data() {
160+
let path = Path::new("tests")
161+
.join("reftest")
162+
.join("images")
163+
.join("ycck.jpg");
164+
165+
let mut decoder = jpeg::Decoder::new(File::open(&path).unwrap());
166+
decoder.decode().unwrap();
167+
168+
let xmp_data = decoder.xmp_data().unwrap();
169+
assert_eq!(&xmp_data[0..9], b"<?xpacket");
170+
}

0 commit comments

Comments
 (0)