Extract Exif attribute information segment from a JPEG file.

This commit is contained in:
KAMADA Ken'ichi 2016-10-06 22:36:23 +09:00
parent a7d79b3f01
commit e6e759af11
4 changed files with 247 additions and 6 deletions

75
src/error.rs Normal file
View File

@ -0,0 +1,75 @@
//
// 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::error;
use std::fmt;
use std::io;
/// An error type returned when parsing Exif data.
#[derive(Debug)]
pub enum Error {
/// Input data was malformed or truncated.
InvalidFormat(&'static str),
/// Input data could not be read due to an I/O error and
/// a `std::io::Error` value is associated with this variant.
Io(io::Error),
/// Exif attribute information was not found.
NotFound(&'static str),
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::Io(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::InvalidFormat(msg) => f.write_str(msg),
Error::Io(ref err) => err.fmt(f),
Error::NotFound(msg) => f.write_str(msg),
}
}
}
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::InvalidFormat(msg) => msg,
Error::Io(ref err) => err.description(),
Error::NotFound(msg) => msg,
}
}
fn cause(&self) -> Option<&error::Error> {
match *self {
Error::InvalidFormat(_) => None,
Error::Io(ref err) => Some(err),
Error::NotFound(_) => None,
}
}
}

100
src/jpeg.rs Normal file
View File

@ -0,0 +1,100 @@
//
// 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 error::Error;
use util::read8;
use util::read16;
mod marker {
// The first byte of a marker.
pub const P: u8 = 0xff;
// Marker codes.
pub const Z: u8 = 0x00; // Not a marker but a byte stuffing.
pub const TEM: u8 = 0x01;
pub const RST0: u8 = 0xd0;
pub const RST7: u8 = 0xd7;
pub const SOI: u8 = 0xd8;
pub const EOI: u8 = 0xd9;
pub const SOS: u8 = 0xda;
pub const APP1: u8 = 0xe1;
}
// Exif identifier code "Exif\0\0". [EXIF23 4.7.2]
const EXIF_ID: [u8; 6] = [0x45, 0x78, 0x69, 0x66, 0x00, 0x00];
// Get the Exif attribute information segment from a JPEG file.
pub fn get_exif_attr<R>(reader: &mut R)
-> Result<Vec<u8>, Error> where R: io::BufRead {
match get_exif_attr_sub(reader) {
Err(Error::Io(ref e)) if e.kind() == io::ErrorKind::UnexpectedEof =>
Err(Error::InvalidFormat("Broken JPEG file")),
r => r,
}
}
fn get_exif_attr_sub<R>(reader: &mut R)
-> Result<Vec<u8>, Error> where R: io::BufRead {
let mut soi = [0u8; 2];
let mut code;
try!(reader.read_exact(&mut soi));
if soi != [marker::P, marker::SOI] {
return Err(Error::InvalidFormat("Not a JPEG file"));
}
loop {
// Find a marker prefix. Discard non-ff bytes, which appear if
// we are in the scan data after SOS or we are out of sync.
try!(reader.read_until(marker::P, &mut Vec::new()));
// Get a marker code.
loop {
code = try!(read8(reader));
if code != marker::P { break; }
}
// Continue or return early on stand-alone markers.
match code {
marker::Z | marker::TEM | marker::RST0...marker::RST7 => continue,
marker::SOI => return Err(Error::InvalidFormat("Unexpected SOI")),
marker::EOI => return Err(Error::NotFound("No Exif data found")),
_ => {},
}
// Read marker segments.
let seglen = try!(read16(reader));
if seglen < 2 {
return Err(Error::InvalidFormat("Invalid segment length"));
}
let mut seg = Vec::new();
try!(reader.by_ref().take(seglen as u64 - 2).read_to_end(&mut seg));
if code == marker::APP1 && seg.starts_with(&EXIF_ID) {
return Ok(seg.split_off(EXIF_ID.len()));
}
if code == marker::SOS {
// Skipping the scan data is handled in the main loop,
// so there is nothing to do here.
}
}
}

View File

@ -1,6 +1,34 @@
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
}
}
//
// 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.
//
//! Exif parsing library written in pure Rust.
pub use error::Error;
pub use jpeg::get_exif_attr as get_exif_attr_from_jpeg;
mod error;
mod jpeg;
mod util;

38
src/util.rs Normal file
View File

@ -0,0 +1,38 @@
//
// 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;
pub fn read8<R>(reader: &mut R) -> Result<u8, io::Error> where R: io::Read {
let mut buf: [u8; 1] = unsafe { ::std::mem::uninitialized() };
reader.read_exact(&mut buf).and(Ok(buf[0]))
}
pub fn read16<R>(reader: &mut R) -> Result<u16, io::Error> where R: io::Read {
let mut buf: [u8; 2] = unsafe { ::std::mem::uninitialized() };
try!(reader.read_exact(&mut buf));
Ok(((buf[0] as u16) << 8) + buf[1] as u16)
}