1 | use std::error::Error; |
2 | use std::fmt; |
3 | use std::fmt::Display; |
4 | use std::io; |
5 | use std::str; |
6 | use std::string; |
7 | use std::sync::Arc; |
8 | |
9 | use jpeg::UnsupportedFeature; |
10 | |
11 | use crate::decoder::{ifd::Value, ChunkType}; |
12 | use crate::tags::{ |
13 | CompressionMethod, PhotometricInterpretation, PlanarConfiguration, SampleFormat, Tag, |
14 | }; |
15 | use crate::ColorType; |
16 | |
17 | use crate::weezl::LzwError; |
18 | |
19 | /// Tiff error kinds. |
20 | #[derive (Debug)] |
21 | pub enum TiffError { |
22 | /// The Image is not formatted properly. |
23 | FormatError(TiffFormatError), |
24 | |
25 | /// The Decoder does not support features required by the image. |
26 | UnsupportedError(TiffUnsupportedError), |
27 | |
28 | /// An I/O Error occurred while decoding the image. |
29 | IoError(io::Error), |
30 | |
31 | /// The Limits of the Decoder is exceeded. |
32 | LimitsExceeded, |
33 | |
34 | /// An integer conversion to or from a platform size failed, either due to |
35 | /// limits of the platform size or limits of the format. |
36 | IntSizeError, |
37 | |
38 | /// The image does not support the requested operation |
39 | UsageError(UsageError), |
40 | } |
41 | |
42 | /// The image is not formatted properly. |
43 | /// |
44 | /// This indicates that the encoder producing the image might behave incorrectly or that the input |
45 | /// file has been corrupted. |
46 | /// |
47 | /// The list of variants may grow to incorporate errors of future features. Matching against this |
48 | /// exhaustively is not covered by interface stability guarantees. |
49 | #[derive (Debug, Clone, PartialEq)] |
50 | #[non_exhaustive ] |
51 | pub enum TiffFormatError { |
52 | TiffSignatureNotFound, |
53 | TiffSignatureInvalid, |
54 | ImageFileDirectoryNotFound, |
55 | InconsistentSizesEncountered, |
56 | UnexpectedCompressedData { |
57 | actual_bytes: usize, |
58 | required_bytes: usize, |
59 | }, |
60 | InconsistentStripSamples { |
61 | actual_samples: usize, |
62 | required_samples: usize, |
63 | }, |
64 | InvalidDimensions(u32, u32), |
65 | InvalidTag, |
66 | InvalidTagValueType(Tag), |
67 | RequiredTagNotFound(Tag), |
68 | UnknownPredictor(u16), |
69 | UnknownPlanarConfiguration(u16), |
70 | ByteExpected(Value), |
71 | UnsignedIntegerExpected(Value), |
72 | SignedIntegerExpected(Value), |
73 | Format(String), |
74 | RequiredTagEmpty(Tag), |
75 | StripTileTagConflict, |
76 | CycleInOffsets, |
77 | JpegDecoder(JpegDecoderError), |
78 | SamplesPerPixelIsZero, |
79 | } |
80 | |
81 | impl fmt::Display for TiffFormatError { |
82 | fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
83 | use self::TiffFormatError::*; |
84 | match *self { |
85 | TiffSignatureNotFound => write!(fmt, "TIFF signature not found." ), |
86 | TiffSignatureInvalid => write!(fmt, "TIFF signature invalid." ), |
87 | ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found." ), |
88 | InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered." ), |
89 | UnexpectedCompressedData { |
90 | actual_bytes, |
91 | required_bytes, |
92 | } => { |
93 | write!( |
94 | fmt, |
95 | "Decompression returned different amount of bytes than expected: got {}, expected {}." , |
96 | actual_bytes, required_bytes |
97 | ) |
98 | } |
99 | InconsistentStripSamples { |
100 | actual_samples, |
101 | required_samples, |
102 | } => { |
103 | write!( |
104 | fmt, |
105 | "Inconsistent elements in strip: got {}, expected {}." , |
106 | actual_samples, required_samples |
107 | ) |
108 | } |
109 | InvalidDimensions(width, height) => write!(fmt, "Invalid dimensions: {}x {}." , width, height), |
110 | InvalidTag => write!(fmt, "Image contains invalid tag." ), |
111 | InvalidTagValueType(ref tag) => { |
112 | write!(fmt, "Tag ` {:?}` did not have the expected value type." , tag) |
113 | } |
114 | RequiredTagNotFound(ref tag) => write!(fmt, "Required tag ` {:?}` not found." , tag), |
115 | UnknownPredictor(ref predictor) => { |
116 | write!(fmt, "Unknown predictor “ {}” encountered" , predictor) |
117 | } |
118 | UnknownPlanarConfiguration(ref planar_config) => { |
119 | write!(fmt, "Unknown planar configuration “ {}” encountered" , planar_config) |
120 | } |
121 | ByteExpected(ref val) => write!(fmt, "Expected byte, {:?} found." , val), |
122 | UnsignedIntegerExpected(ref val) => { |
123 | write!(fmt, "Expected unsigned integer, {:?} found." , val) |
124 | } |
125 | SignedIntegerExpected(ref val) => { |
126 | write!(fmt, "Expected signed integer, {:?} found." , val) |
127 | } |
128 | Format(ref val) => write!(fmt, "Invalid format: {:?}." , val), |
129 | RequiredTagEmpty(ref val) => write!(fmt, "Required tag {:?} was empty." , val), |
130 | StripTileTagConflict => write!(fmt, "File should contain either (StripByteCounts and StripOffsets) or (TileByteCounts and TileOffsets), other combination was found." ), |
131 | CycleInOffsets => write!(fmt, "File contained a cycle in the list of IFDs" ), |
132 | JpegDecoder(ref error) => write!(fmt, " {}" , error), |
133 | SamplesPerPixelIsZero => write!(fmt, "Samples per pixel is zero" ), |
134 | } |
135 | } |
136 | } |
137 | |
138 | /// The Decoder does not support features required by the image. |
139 | /// |
140 | /// This only captures known failures for which the standard either does not require support or an |
141 | /// implementation has been planned but not yet completed. Some variants may become unused over |
142 | /// time and will then get deprecated before being removed. |
143 | /// |
144 | /// The list of variants may grow. Matching against this exhaustively is not covered by interface |
145 | /// stability guarantees. |
146 | #[derive (Debug, Clone, PartialEq, Eq, Hash)] |
147 | #[non_exhaustive ] |
148 | pub enum TiffUnsupportedError { |
149 | FloatingPointPredictor(ColorType), |
150 | HorizontalPredictor(ColorType), |
151 | InconsistentBitsPerSample(Vec<u8>), |
152 | InterpretationWithBits(PhotometricInterpretation, Vec<u8>), |
153 | UnknownInterpretation, |
154 | UnknownCompressionMethod, |
155 | UnsupportedCompressionMethod(CompressionMethod), |
156 | UnsupportedSampleDepth(u8), |
157 | UnsupportedSampleFormat(Vec<SampleFormat>), |
158 | UnsupportedColorType(ColorType), |
159 | UnsupportedBitsPerChannel(u8), |
160 | UnsupportedPlanarConfig(Option<PlanarConfiguration>), |
161 | UnsupportedDataType, |
162 | UnsupportedInterpretation(PhotometricInterpretation), |
163 | UnsupportedJpegFeature(UnsupportedFeature), |
164 | } |
165 | |
166 | impl fmt::Display for TiffUnsupportedError { |
167 | fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
168 | use self::TiffUnsupportedError::*; |
169 | match *self { |
170 | FloatingPointPredictor(color_type) => write!( |
171 | fmt, |
172 | "Floating point predictor for {:?} is unsupported." , |
173 | color_type |
174 | ), |
175 | HorizontalPredictor(color_type) => write!( |
176 | fmt, |
177 | "Horizontal predictor for {:?} is unsupported." , |
178 | color_type |
179 | ), |
180 | InconsistentBitsPerSample(ref bits_per_sample) => { |
181 | write!(fmt, "Inconsistent bits per sample: {:?}." , bits_per_sample) |
182 | } |
183 | InterpretationWithBits(ref photometric_interpretation, ref bits_per_sample) => write!( |
184 | fmt, |
185 | " {:?} with {:?} bits per sample is unsupported" , |
186 | photometric_interpretation, bits_per_sample |
187 | ), |
188 | UnknownInterpretation => write!( |
189 | fmt, |
190 | "The image is using an unknown photometric interpretation." |
191 | ), |
192 | UnknownCompressionMethod => write!(fmt, "Unknown compression method." ), |
193 | UnsupportedCompressionMethod(method) => { |
194 | write!(fmt, "Compression method {:?} is unsupported" , method) |
195 | } |
196 | UnsupportedSampleDepth(samples) => { |
197 | write!(fmt, " {} samples per pixel is unsupported." , samples) |
198 | } |
199 | UnsupportedSampleFormat(ref formats) => { |
200 | write!(fmt, "Sample format {:?} is unsupported." , formats) |
201 | } |
202 | UnsupportedColorType(color_type) => { |
203 | write!(fmt, "Color type {:?} is unsupported" , color_type) |
204 | } |
205 | UnsupportedBitsPerChannel(bits) => { |
206 | write!(fmt, " {} bits per channel not supported" , bits) |
207 | } |
208 | UnsupportedPlanarConfig(config) => { |
209 | write!(fmt, "Unsupported planar configuration “ {:?}”." , config) |
210 | } |
211 | UnsupportedDataType => write!(fmt, "Unsupported data type." ), |
212 | UnsupportedInterpretation(interpretation) => { |
213 | write!( |
214 | fmt, |
215 | "Unsupported photometric interpretation \"{:?}\"." , |
216 | interpretation |
217 | ) |
218 | } |
219 | UnsupportedJpegFeature(ref unsupported_feature) => { |
220 | write!(fmt, "Unsupported JPEG feature {:?}" , unsupported_feature) |
221 | } |
222 | } |
223 | } |
224 | } |
225 | |
226 | /// User attempted to use the Decoder in a way that is incompatible with a specific image. |
227 | /// |
228 | /// For example: attempting to read a tile from a stripped image. |
229 | #[derive (Debug)] |
230 | pub enum UsageError { |
231 | InvalidChunkType(ChunkType, ChunkType), |
232 | InvalidChunkIndex(u32), |
233 | } |
234 | |
235 | impl fmt::Display for UsageError { |
236 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
237 | use self::UsageError::*; |
238 | match *self { |
239 | InvalidChunkType(expected: ChunkType, actual: ChunkType) => { |
240 | write!( |
241 | fmt, |
242 | "Requested operation is only valid for images with chunk encoding of type: {:?}, got {:?}." , |
243 | expected, actual |
244 | ) |
245 | } |
246 | InvalidChunkIndex(index: u32) => write!(fmt, "Image chunk index ( {}) requested." , index), |
247 | } |
248 | } |
249 | } |
250 | |
251 | impl fmt::Display for TiffError { |
252 | fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
253 | match *self { |
254 | TiffError::FormatError(ref e: &TiffFormatError) => write!(fmt, "Format error: {}" , e), |
255 | TiffError::UnsupportedError(ref f: &TiffUnsupportedError) => write!( |
256 | fmt, |
257 | "The Decoder does not support the \ |
258 | image format ` {}`" , |
259 | f |
260 | ), |
261 | TiffError::IoError(ref e: &Error) => e.fmt(fmt), |
262 | TiffError::LimitsExceeded => write!(fmt, "The Decoder limits are exceeded" ), |
263 | TiffError::IntSizeError => write!(fmt, "Platform or format size limits exceeded" ), |
264 | TiffError::UsageError(ref e: &UsageError) => write!(fmt, "Usage error: {}" , e), |
265 | } |
266 | } |
267 | } |
268 | |
269 | impl Error for TiffError { |
270 | fn description(&self) -> &str { |
271 | match *self { |
272 | TiffError::FormatError(..) => "Format error" , |
273 | TiffError::UnsupportedError(..) => "Unsupported error" , |
274 | TiffError::IoError(..) => "IO error" , |
275 | TiffError::LimitsExceeded => "Decoder limits exceeded" , |
276 | TiffError::IntSizeError => "Platform or format size limits exceeded" , |
277 | TiffError::UsageError(..) => "Invalid usage" , |
278 | } |
279 | } |
280 | |
281 | fn cause(&self) -> Option<&dyn Error> { |
282 | match *self { |
283 | TiffError::IoError(ref e: &Error) => Some(e), |
284 | _ => None, |
285 | } |
286 | } |
287 | } |
288 | |
289 | impl From<io::Error> for TiffError { |
290 | fn from(err: io::Error) -> TiffError { |
291 | TiffError::IoError(err) |
292 | } |
293 | } |
294 | |
295 | impl From<str::Utf8Error> for TiffError { |
296 | fn from(_err: str::Utf8Error) -> TiffError { |
297 | TiffError::FormatError(TiffFormatError::InvalidTag) |
298 | } |
299 | } |
300 | |
301 | impl From<string::FromUtf8Error> for TiffError { |
302 | fn from(_err: string::FromUtf8Error) -> TiffError { |
303 | TiffError::FormatError(TiffFormatError::InvalidTag) |
304 | } |
305 | } |
306 | |
307 | impl From<TiffFormatError> for TiffError { |
308 | fn from(err: TiffFormatError) -> TiffError { |
309 | TiffError::FormatError(err) |
310 | } |
311 | } |
312 | |
313 | impl From<TiffUnsupportedError> for TiffError { |
314 | fn from(err: TiffUnsupportedError) -> TiffError { |
315 | TiffError::UnsupportedError(err) |
316 | } |
317 | } |
318 | |
319 | impl From<UsageError> for TiffError { |
320 | fn from(err: UsageError) -> TiffError { |
321 | TiffError::UsageError(err) |
322 | } |
323 | } |
324 | |
325 | impl From<std::num::TryFromIntError> for TiffError { |
326 | fn from(_err: std::num::TryFromIntError) -> TiffError { |
327 | TiffError::IntSizeError |
328 | } |
329 | } |
330 | |
331 | impl From<LzwError> for TiffError { |
332 | fn from(err: LzwError) -> TiffError { |
333 | match err { |
334 | LzwError::InvalidCode => TiffError::FormatError(TiffFormatError::Format(String::from( |
335 | "LZW compressed data corrupted" , |
336 | ))), |
337 | } |
338 | } |
339 | } |
340 | |
341 | #[derive (Debug, Clone)] |
342 | pub struct JpegDecoderError { |
343 | inner: Arc<jpeg::Error>, |
344 | } |
345 | |
346 | impl JpegDecoderError { |
347 | fn new(error: jpeg::Error) -> Self { |
348 | Self { |
349 | inner: Arc::new(data:error), |
350 | } |
351 | } |
352 | } |
353 | |
354 | impl PartialEq for JpegDecoderError { |
355 | fn eq(&self, other: &Self) -> bool { |
356 | Arc::ptr_eq(&self.inner, &other.inner) |
357 | } |
358 | } |
359 | |
360 | impl Display for JpegDecoderError { |
361 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
362 | self.inner.fmt(f) |
363 | } |
364 | } |
365 | |
366 | impl From<JpegDecoderError> for TiffError { |
367 | fn from(error: JpegDecoderError) -> Self { |
368 | TiffError::FormatError(TiffFormatError::JpegDecoder(error)) |
369 | } |
370 | } |
371 | |
372 | impl From<jpeg::Error> for TiffError { |
373 | fn from(error: jpeg::Error) -> Self { |
374 | JpegDecoderError::new(error).into() |
375 | } |
376 | } |
377 | |
378 | /// Result of an image decoding/encoding process |
379 | pub type TiffResult<T> = Result<T, TiffError>; |
380 | |