Extract Exif attribute information segment from a JPEG file.
This commit is contained in:
parent
a7d79b3f01
commit
e6e759af11
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
}
|
40
src/lib.rs
40
src/lib.rs
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
}
|
Loading…
Reference in New Issue