From 906a25cb3106a16f9dec77045367e0f78a39aef6 Mon Sep 17 00:00:00 2001 From: KAMADA Ken'ichi Date: Thu, 12 Jan 2017 21:51:11 +0900 Subject: [PATCH] Add parse_image(), which accepts a JPEG or TIFF image. --- src/image.rs | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/jpeg.rs | 7 +++++++ src/lib.rs | 8 ++++--- src/tiff.rs | 6 ++++++ 4 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 src/image.rs diff --git a/src/image.rs b/src/image.rs new file mode 100644 index 0000000..cdb21af --- /dev/null +++ b/src/image.rs @@ -0,0 +1,59 @@ +// +// Copyright (c) 2016 KAMADA Ken'ichi. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. +// + +use std::io; +use std::io::Read; +use std::mem; + +use error::Error; +use jpeg; +use tiff; +use tiff::Field; + +/// Parse the Exif attributes in a JPEG or TIFF image data. +/// +/// Returns a Vec of Exif fields and a bool. +/// The boolean value is true if the data is little endian. +/// If an error occurred, `exif::Error` is returned. +/// +/// The `buf` must be an empty `Vec` when this function is called. +/// The raw Exif data is read into it. +pub fn parse_image<'a, R>(mut reader: &mut R, mut buf: &'a mut Vec) + -> Result<(Vec>, bool), Error> + where R: io::BufRead +{ + try!(reader.by_ref().take(4).read_to_end(buf)); + if jpeg::is_jpeg(buf) { + let exif_buf = try!(jpeg::get_exif_attr( + &mut buf.as_mut_slice().chain(reader))); + mem::replace(buf, exif_buf); + } else if tiff::is_tiff(buf) { + try!(reader.read_to_end(&mut buf)); + } else { + return Err(Error::InvalidFormat("Unknown image format")); + } + tiff::parse_exif(buf) +} diff --git a/src/jpeg.rs b/src/jpeg.rs index 679fc63..409adcf 100644 --- a/src/jpeg.rs +++ b/src/jpeg.rs @@ -45,6 +45,9 @@ mod marker { pub const APP1: u8 = 0xe1; } +// SOI marker as the JPEG header. +const JPEG_SIG: [u8; 2] = [marker::P, marker::SOI]; + // Exif identifier code "Exif\0\0". [EXIF23 4.7.2] const EXIF_ID: [u8; 6] = [0x45, 0x78, 0x69, 0x66, 0x00, 0x00]; @@ -99,6 +102,10 @@ fn get_exif_attr_sub(reader: &mut R) } } +pub fn is_jpeg(buf: &[u8]) -> bool { + buf.starts_with(&JPEG_SIG) +} + #[cfg(test)] mod tests { use std::io::Cursor; diff --git a/src/lib.rs b/src/lib.rs index a1a079f..2516fef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,19 +28,20 @@ //! //! # Examples //! -//! An example to parse a JPEG file: +//! An example to parse a JPEG/TIFF file: //! //! ``` //! let file = std::fs::File::open("tests/exif.jpg").unwrap(); //! let mut reader = std::io::BufReader::new(&file); -//! let buf = exif::get_exif_attr_from_jpeg(&mut reader).unwrap(); -//! let (fields, _) = exif::parse_exif(&buf).unwrap(); +//! let mut buf = Vec::new(); +//! let (fields, _) = exif::parse_image(&mut reader, &mut buf).unwrap(); //! for f in fields { //! println!("{} {} {:?}", f.tag, f.thumbnail, f.value); //! } //! ``` pub use error::Error; +pub use image::parse_image; pub use jpeg::get_exif_attr as get_exif_attr_from_jpeg; pub use tag::{Context, Tag}; pub use tiff::Field; @@ -54,6 +55,7 @@ mod tmacro; mod endian; mod error; +mod image; mod jpeg; pub mod tag; mod tiff; diff --git a/src/tiff.rs b/src/tiff.rs index c297ea0..32ffa2d 100644 --- a/src/tiff.rs +++ b/src/tiff.rs @@ -35,6 +35,8 @@ use value::get_type_info; const TIFF_BE: u16 = 0x4d4d; const TIFF_LE: u16 = 0x4949; const TIFF_FORTY_TWO: u16 = 0x002a; +const TIFF_BE_SIG: [u8; 4] = [0x4d, 0x4d, 0x00, 0x2a]; +const TIFF_LE_SIG: [u8; 4] = [0x49, 0x49, 0x2a, 0x00]; /// A TIFF field. #[derive(Debug)] @@ -149,6 +151,10 @@ fn parse_ifd(data: &[u8], offset: usize, ctx: Context, thumbnail: bool) Ok(fields) } +pub fn is_tiff(buf: &[u8]) -> bool { + buf.starts_with(&TIFF_BE_SIG) || buf.starts_with(&TIFF_LE_SIG) +} + #[cfg(test)] mod tests { use error::Error;