1 #![allow(clippy::upper_case_acronyms)]
2 
3 //! RExif is a native Rust create, written to extract EXIF data from JPEG and TIFF images.
4 //!
5 //! Note that it is in very early stages of development. Any sort of feedback is welcome!
6 //!
7 //! The crate contains a
8 //! sample binary called 'rexiftool' that accepts files as arguments and prints the EXIF data. It gives
9 //! a rough idea on how to use the crate. Get some sample images and run
10 //!
11 //!
12 //! `cargo run [image file 1] [image file 2] ...`
13 //!
14 //!
15 //! To learn to use this crate, start by the documentation of function `parse_file()`,
16 //! and the struct `ExifData` that is returned by the parser. The rest falls more or less into place.
17 //!
18 //! Code sample lightly edited from src/bin.rs:
19 //!
20 //! ```
21 //! use std::error::Error;
22 //!
23 //! let file_name = "foo.jpg";
24 //! match rexif::parse_file(&file_name) {
25 //!     Ok(exif) => {
26 //!         println!("{} {} exif entries: {}", file_name, exif.mime, exif.entries.len());
27 //!         for entry in &exif.entries {
28 //!             println!("\t{}: {}", entry.tag, entry.value_more_readable);
29 //!         }
30 //!     },
31 //!     Err(e) => {
32 //!         print!("Error in {}: {}", &file_name, e)
33 //!     }
34 //! }
35 //! ```
36 
37 use std::fs::File;
38 use std::io::{Read, Seek, SeekFrom};
39 use std::path::Path;
40 
41 mod lowlevel;
42 mod rational;
43 pub use self::rational::*;
44 mod types;
45 pub use self::types::*;
46 mod types_impl;
47 pub use self::types_impl::*;
48 mod image;
49 use self::image::*;
50 mod ifdformat;
51 mod tiff;
52 use self::tiff::*;
53 mod exif;
54 mod exifpost;
55 mod exifreadable;
56 
57 /// Parse a byte buffer that should contain a TIFF or JPEG image.
58 /// Tries to detect format and parse EXIF data.
59 ///
60 /// Prints warnings to stderr.
parse_buffer(contents: &[u8]) -> ExifResult61 pub fn parse_buffer(contents: &[u8]) -> ExifResult {
62     let (res, warnings) = parse_buffer_quiet(contents);
63     warnings.into_iter().for_each(|w| eprintln!("{}", w));
64     res
65 }
66 
67 /// Parse a byte buffer that should contain a TIFF or JPEG image.
68 /// Tries to detect format and parse EXIF data.
69 ///
70 /// Returns warnings alongside result.
parse_buffer_quiet(contents: &[u8]) -> (ExifResult, Vec<String>)71 pub fn parse_buffer_quiet(contents: &[u8]) -> (ExifResult, Vec<String>) {
72     let mime = detect_type(contents);
73     let mut warnings = vec![];
74     let (entries, le) = match mime {
75         FileType::Unknown => return (Err(ExifError::FileTypeUnknown), warnings),
76         FileType::TIFF => parse_tiff(contents, &mut warnings),
77         FileType::JPEG => {
78             match find_embedded_tiff_in_jpeg(contents).and_then(|(offset, size)| {
79                 Ok(parse_tiff(&contents[offset..offset + size], &mut warnings))
80             }) {
81                 Ok(r) => r,
82                 Err(e) => return (Err(e), warnings)
83             }
84         }
85     };
86 
87     (
88         entries.map(|entries| ExifData {
89             mime: mime.as_str(),
90             entries,
91             le
92         }),
93         warnings,
94     )
95 }
96 
97 /// Try to read and parse an open file that is expected to contain an image
read_file(f: &mut File) -> ExifResult98 pub fn read_file(f: &mut File) -> ExifResult {
99     f.seek(SeekFrom::Start(0))?;
100 
101     // TODO: should read only the relevant parts of a file,
102     // and pass a StringIO-like object instead of a Vec buffer
103 
104     let mut contents: Vec<u8> = Vec::new();
105     f.read_to_end(&mut contents)?;
106     parse_buffer(&contents)
107 }
108 
109 /// Opens an image (passed as a file name), tries to read and parse it.
parse_file<P: AsRef<Path>>(fname: P) -> ExifResult110 pub fn parse_file<P: AsRef<Path>>(fname: P) -> ExifResult {
111     read_file(&mut File::open(fname)?)
112 }
113