1 use std::fs::File;
2 use std::io::{self, BufRead, BufReader, Cursor, Read, Seek, SeekFrom};
3 use std::path::Path;
4 
5 use crate::dynimage::DynamicImage;
6 use crate::image::ImageFormat;
7 use crate::error::{ImageFormatHint, UnsupportedError, UnsupportedErrorKind};
8 use crate::{ImageError, ImageResult};
9 
10 use super::free_functions;
11 
12 /// A multi-format image reader.
13 ///
14 /// Wraps an input reader to facilitate automatic detection of an image's format, appropriate
15 /// decoding method, and dispatches into the set of supported [`ImageDecoder`] implementations.
16 ///
17 /// ## Usage
18 ///
19 /// Opening a file, deducing the format based on the file path automatically, and trying to decode
20 /// the image contained can be performed by constructing the reader and immediately consuming it.
21 ///
22 /// ```no_run
23 /// # use image::ImageError;
24 /// # use image::io::Reader;
25 /// # fn main() -> Result<(), ImageError> {
26 /// let image = Reader::open("path/to/image.png")?
27 ///     .decode()?;
28 /// # Ok(()) }
29 /// ```
30 ///
31 /// It is also possible to make a guess based on the content. This is especially handy if the
32 /// source is some blob in memory and you have constructed the reader in another way. Here is an
33 /// example with a `pnm` black-and-white subformat that encodes its pixel matrix with ascii values.
34 ///
35 #[cfg_attr(feature = "pnm", doc = "```")]
36 #[cfg_attr(not(feature = "pnm"), doc = "```no_run")]
37 /// # use image::ImageError;
38 /// # use image::io::Reader;
39 /// # fn main() -> Result<(), ImageError> {
40 /// use std::io::Cursor;
41 /// use image::ImageFormat;
42 ///
43 /// let raw_data = b"P1 2 2\n\
44 ///     0 1\n\
45 ///     1 0\n";
46 ///
47 /// let mut reader = Reader::new(Cursor::new(raw_data))
48 ///     .with_guessed_format()
49 ///     .expect("Cursor io never fails");
50 /// assert_eq!(reader.format(), Some(ImageFormat::Pnm));
51 ///
52 /// let image = reader.decode()?;
53 /// # Ok(()) }
54 /// ```
55 ///
56 /// As a final fallback or if only a specific format must be used, the reader always allows manual
57 /// specification of the supposed image format with [`set_format`].
58 ///
59 /// [`set_format`]: #method.set_format
60 /// [`ImageDecoder`]: ../trait.ImageDecoder.html
61 pub struct Reader<R: Read> {
62     /// The reader.
63     inner: R,
64     /// The format, if one has been set or deduced.
65     format: Option<ImageFormat>,
66 }
67 
68 impl<R: Read> Reader<R> {
69     /// Create a new image reader without a preset format.
70     ///
71     /// It is possible to guess the format based on the content of the read object with
72     /// [`with_guessed_format`], or to set the format directly with [`set_format`].
73     ///
74     /// [`with_guessed_format`]: #method.with_guessed_format
75     /// [`set_format`]: method.set_format
new(reader: R) -> Self76     pub fn new(reader: R) -> Self {
77         Reader {
78             inner: reader,
79             format: None,
80         }
81     }
82 
83     /// Construct a reader with specified format.
with_format(reader: R, format: ImageFormat) -> Self84     pub fn with_format(reader: R, format: ImageFormat) -> Self {
85         Reader {
86             inner: reader,
87             format: Some(format),
88         }
89     }
90 
91     /// Get the currently determined format.
format(&self) -> Option<ImageFormat>92     pub fn format(&self) -> Option<ImageFormat> {
93         self.format
94     }
95 
96     /// Supply the format as which to interpret the read image.
set_format(&mut self, format: ImageFormat)97     pub fn set_format(&mut self, format: ImageFormat) {
98         self.format = Some(format);
99     }
100 
101     /// Remove the current information on the image format.
102     ///
103     /// Note that many operations require format information to be present and will return e.g. an
104     /// `ImageError::Unsupported` when the image format has not been set.
clear_format(&mut self)105     pub fn clear_format(&mut self) {
106         self.format = None;
107     }
108 
109     /// Unwrap the reader.
into_inner(self) -> R110     pub fn into_inner(self) -> R {
111         self.inner
112     }
113 }
114 
115 impl Reader<BufReader<File>> {
116     /// Open a file to read, format will be guessed from path.
117     ///
118     /// This will not attempt any io operation on the opened file.
119     ///
120     /// If you want to inspect the content for a better guess on the format, which does not depend
121     /// on file extensions, follow this call with a call to [`with_guessed_format`].
122     ///
123     /// [`with_guessed_format`]: #method.with_guessed_format
open<P>(path: P) -> io::Result<Self> where P: AsRef<Path>124     pub fn open<P>(path: P) -> io::Result<Self> where P: AsRef<Path> {
125         Self::open_impl(path.as_ref())
126     }
127 
open_impl(path: &Path) -> io::Result<Self>128     fn open_impl(path: &Path) -> io::Result<Self> {
129         let file = File::open(path)?;
130         Ok(Reader {
131             inner: BufReader::new(file),
132             format: ImageFormat::from_path(path).ok(),
133         })
134     }
135 }
136 
137 impl<R: BufRead + Seek> Reader<R> {
138     /// Make a format guess based on the content, replacing it on success.
139     ///
140     /// Returns `Ok` with the guess if no io error occurs. Additionally, replaces the current
141     /// format if the guess was successful. If the guess was unable to determine a format then
142     /// the current format of the reader is unchanged.
143     ///
144     /// Returns an error if the underlying reader fails. The format is unchanged. The error is a
145     /// `std::io::Error` and not `ImageError` since the only error case is an error when the
146     /// underlying reader seeks.
147     ///
148     /// When an error occurs, the reader may not have been properly reset and it is potentially
149     /// hazardous to continue with more io.
150     ///
151     /// ## Usage
152     ///
153     /// This supplements the path based type deduction from [`open`](Reader::open) with content based deduction.
154     /// This is more common in Linux and UNIX operating systems and also helpful if the path can
155     /// not be directly controlled.
156     ///
157     /// ```no_run
158     /// # use image::ImageError;
159     /// # use image::io::Reader;
160     /// # fn main() -> Result<(), ImageError> {
161     /// let image = Reader::open("image.unknown")?
162     ///     .with_guessed_format()?
163     ///     .decode()?;
164     /// # Ok(()) }
165     /// ```
with_guessed_format(mut self) -> io::Result<Self>166     pub fn with_guessed_format(mut self) -> io::Result<Self> {
167         let format = self.guess_format()?;
168         // Replace format if found, keep current state if not.
169         self.format = format.or(self.format);
170         Ok(self)
171     }
172 
guess_format(&mut self) -> io::Result<Option<ImageFormat>>173     fn guess_format(&mut self) -> io::Result<Option<ImageFormat>> {
174         let mut start = [0; 16];
175 
176         // Save current offset, read start, restore offset.
177         let cur = self.inner.seek(SeekFrom::Current(0))?;
178         let len = io::copy(
179             // Accept shorter files but read at most 16 bytes.
180             &mut self.inner.by_ref().take(16),
181             &mut Cursor::new(&mut start[..]))?;
182         self.inner.seek(SeekFrom::Start(cur))?;
183 
184         Ok(free_functions::guess_format_impl(&start[..len as usize]))
185     }
186 
187     /// Read the image dimensions.
188     ///
189     /// Uses the current format to construct the correct reader for the format.
190     ///
191     /// If no format was determined, returns an `ImageError::Unsupported`.
into_dimensions(mut self) -> ImageResult<(u32, u32)>192     pub fn into_dimensions(mut self) -> ImageResult<(u32, u32)> {
193         let format = self.require_format()?;
194         free_functions::image_dimensions_with_format_impl(self.inner, format)
195     }
196 
197     /// Read the image (replaces `load`).
198     ///
199     /// Uses the current format to construct the correct reader for the format.
200     ///
201     /// If no format was determined, returns an `ImageError::Unsupported`.
decode(mut self) -> ImageResult<DynamicImage>202     pub fn decode(mut self) -> ImageResult<DynamicImage> {
203         let format = self.require_format()?;
204         free_functions::load(self.inner, format)
205     }
206 
require_format(&mut self) -> ImageResult<ImageFormat>207     fn require_format(&mut self) -> ImageResult<ImageFormat> {
208         self.format.ok_or_else(||
209             ImageError::Unsupported(UnsupportedError::from_format_and_kind(
210                 ImageFormatHint::Unknown,
211                 UnsupportedErrorKind::Format(ImageFormatHint::Unknown))))
212     }
213 }
214