1use std::error::Error;
4use std::fmt::Debug;
5use std::{fmt, io, str, string};
6
7use jpeg::UnsupportedFeature;
8use thiserror::Error;
9
10use crate::tag_value::TagValue;
11use crate::tags::{
12 CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Predictor, SampleFormat, Tag,
13};
14
15#[derive(Error, Debug)]
17#[non_exhaustive]
18pub enum AsyncTiffError {
19 #[error("End of File: expected to read {0} bytes, got {1}")]
21 EndOfFile(u64, u64),
22
23 #[error("General error: {0}")]
25 General(String),
26
27 #[error("Tile index out of bounds: {0}, {1}")]
29 TileIndexError(u32, u32),
30
31 #[error(transparent)]
33 IOError(#[from] std::io::Error),
34
35 #[error(transparent)]
37 JPEGDecodingError(#[from] jpeg::Error),
38
39 #[cfg(feature = "jpeg2k")]
41 #[error(transparent)]
42 JPEG2kDecodingError(#[from] jpeg2k::error::Error),
43
44 #[cfg(feature = "object_store")]
46 #[error(transparent)]
47 ObjectStore(#[from] object_store::Error),
48
49 #[error(transparent)]
51 InternalTIFFError(#[from] TiffError),
52
53 #[cfg(feature = "reqwest")]
55 #[error(transparent)]
56 ReqwestError(#[from] reqwest::Error),
57
58 #[error(transparent)]
60 External(Box<dyn std::error::Error + Send + Sync>),
61}
62
63pub type AsyncTiffResult<T> = std::result::Result<T, AsyncTiffError>;
65
66#[derive(Debug)]
68#[allow(clippy::enum_variant_names)]
69pub enum TiffError {
70 FormatError(TiffFormatError),
72
73 UnsupportedError(TiffUnsupportedError),
75
76 IoError(io::Error),
78
79 IntSizeError,
82
83 UsageError(UsageError),
85}
86
87#[derive(Debug, Clone, PartialEq)]
95#[non_exhaustive]
96#[expect(missing_docs)]
97pub enum TiffFormatError {
98 TiffSignatureNotFound,
99 TiffSignatureInvalid,
100 ImageFileDirectoryNotFound,
101 InconsistentSizesEncountered,
102 UnexpectedCompressedData {
103 actual_bytes: usize,
104 required_bytes: usize,
105 },
106 InconsistentStripSamples {
107 actual_samples: usize,
108 required_samples: usize,
109 },
110 InvalidDimensions(u32, u32),
111 InvalidTag,
112 InvalidTagValueType(Tag),
113 RequiredTagNotFound(Tag),
114 UnknownPredictor(u16),
115 UnknownPlanarConfiguration(u16),
116 ByteExpected(TagValue),
117 SignedByteExpected(TagValue),
118 ShortExpected(TagValue),
119 SignedShortExpected(TagValue),
120 UnsignedIntegerExpected(TagValue),
121 SignedIntegerExpected(TagValue),
122 Format(String),
123 RequiredTagEmpty(Tag),
124 StripTileTagConflict,
125 CycleInOffsets,
126 SamplesPerPixelIsZero,
127}
128
129impl fmt::Display for TiffFormatError {
130 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
131 use self::TiffFormatError::*;
132 match *self {
133 TiffSignatureNotFound => write!(fmt, "TIFF signature not found."),
134 TiffSignatureInvalid => write!(fmt, "TIFF signature invalid."),
135 ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found."),
136 InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered."),
137 UnexpectedCompressedData {
138 actual_bytes,
139 required_bytes,
140 } => {
141 write!(
142 fmt,
143 "Decompression returned different amount of bytes than expected: got {actual_bytes}, expected {required_bytes}."
144 )
145 }
146 InconsistentStripSamples {
147 actual_samples,
148 required_samples,
149 } => {
150 write!(
151 fmt,
152 "Inconsistent elements in strip: got {actual_samples}, expected {required_samples}."
153 )
154 }
155 InvalidDimensions(width, height) => write!(fmt, "Invalid dimensions: {width}x{height}."),
156 InvalidTag => write!(fmt, "Image contains invalid tag."),
157 InvalidTagValueType(ref tag) => {
158 write!(fmt, "Tag `{tag:?}` did not have the expected value type.")
159 }
160 RequiredTagNotFound(ref tag) => write!(fmt, "Required tag `{tag:?}` not found."),
161 UnknownPredictor(ref predictor) => {
162 write!(fmt, "Unknown predictor “{predictor}” encountered")
163 }
164 UnknownPlanarConfiguration(ref planar_config) => {
165 write!(fmt, "Unknown planar configuration “{planar_config}” encountered")
166 }
167 ByteExpected(ref val) => write!(fmt, "Expected byte, {val:?} found."),
168 SignedByteExpected(ref val) => write!(fmt, "Expected signed byte, {val:?} found."),
169 ShortExpected(ref val) => write!(fmt, "Expected short, {val:?} found."),
170 SignedShortExpected(ref val) => write!(fmt, "Expected signed short, {val:?} found."),
171 UnsignedIntegerExpected(ref val) => {
172 write!(fmt, "Expected unsigned integer, {val:?} found.")
173 }
174 SignedIntegerExpected(ref val) => {
175 write!(fmt, "Expected signed integer, {val:?} found.")
176 }
177 Format(ref val) => write!(fmt, "Invalid format: {val:?}."),
178 RequiredTagEmpty(ref val) => write!(fmt, "Required tag {val:?} was empty."),
179 StripTileTagConflict => write!(fmt, "File should contain either (StripByteCounts and StripOffsets) or (TileByteCounts and TileOffsets), other combination was found."),
180 CycleInOffsets => write!(fmt, "File contained a cycle in the list of IFDs"),
181 SamplesPerPixelIsZero => write!(fmt, "Samples per pixel is zero"),
182 }
183 }
184}
185
186#[derive(Debug, Clone, PartialEq, Eq, Hash)]
195#[expect(missing_docs)]
196#[non_exhaustive]
197pub enum TiffUnsupportedError {
198 InconsistentBitsPerSample(Vec<u8>),
201 InterpretationWithBits(PhotometricInterpretation, Vec<u8>),
202 UnknownInterpretation,
203 UnknownCompressionMethod,
204 UnsupportedCompressionMethod(CompressionMethod),
205 UnsupportedPredictor(Predictor),
206 UnsupportedSampleDepth(u8),
207 UnsupportedSampleFormat(Vec<SampleFormat>),
208 UnsupportedBitsPerChannel(u8),
210 UnsupportedPlanarConfig(Option<PlanarConfiguration>),
211 UnsupportedDataType,
212 UnsupportedInterpretation(PhotometricInterpretation),
213 UnsupportedJpegFeature(UnsupportedFeature),
214 MisalignedTileBoundaries,
215}
216
217impl fmt::Display for TiffUnsupportedError {
218 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
219 use self::TiffUnsupportedError::*;
220 match *self {
221 InconsistentBitsPerSample(ref bits_per_sample) => {
232 write!(fmt, "Inconsistent bits per sample: {bits_per_sample:?}.")
233 }
234 InterpretationWithBits(ref photometric_interpretation, ref bits_per_sample) => write!(
235 fmt,
236 "{photometric_interpretation:?} with {bits_per_sample:?} bits per sample is unsupported"
237 ),
238 UnknownInterpretation => write!(
239 fmt,
240 "The image is using an unknown photometric interpretation."
241 ),
242 UnknownCompressionMethod => write!(fmt, "Unknown compression method."),
243 UnsupportedCompressionMethod(method) => {
244 write!(fmt, "Compression method {method:?} is unsupported")
245 }
246 UnsupportedPredictor(p) => {
247 write!(fmt, "Predictor {p:?} is unsupported")
248 }
249 UnsupportedSampleDepth(samples) => {
250 write!(fmt, "{samples} samples per pixel is unsupported.")
251 }
252 UnsupportedSampleFormat(ref formats) => {
253 write!(fmt, "Sample format {formats:?} is unsupported.")
254 }
255 UnsupportedBitsPerChannel(bits) => {
259 write!(fmt, "{bits} bits per channel not supported")
260 }
261 UnsupportedPlanarConfig(config) => {
262 write!(fmt, "Unsupported planar configuration “{config:?}”.")
263 }
264 UnsupportedDataType => write!(fmt, "Unsupported data type."),
265 UnsupportedInterpretation(interpretation) => {
266 write!(
267 fmt,
268 "Unsupported photometric interpretation \"{interpretation:?}\"."
269 )
270 }
271 UnsupportedJpegFeature(ref unsupported_feature) => {
272 write!(fmt, "Unsupported JPEG feature {unsupported_feature:?}")
273 }
274 MisalignedTileBoundaries => write!(fmt, "Tile rows are not aligned to byte boundaries"),
275 }
276 }
277}
278
279#[expect(missing_docs)]
283#[derive(Debug)]
284pub enum UsageError {
285 InvalidChunkIndex(u32),
287 PredictorCompressionMismatch,
288 PredictorIncompatible,
289 PredictorUnavailable,
290}
291
292impl fmt::Display for UsageError {
293 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
294 use self::UsageError::*;
295 match *self {
296 InvalidChunkIndex(index) => write!(fmt, "Image chunk index ({index}) requested."),
304 PredictorCompressionMismatch => write!(
305 fmt,
306 "The requested predictor is not compatible with the requested compression"
307 ),
308 PredictorIncompatible => write!(
309 fmt,
310 "The requested predictor is not compatible with the image's format"
311 ),
312 PredictorUnavailable => write!(fmt, "The requested predictor is not available"),
313 }
314 }
315}
316
317impl fmt::Display for TiffError {
318 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
319 match *self {
320 TiffError::FormatError(ref e) => write!(fmt, "Format error: {e}"),
321 TiffError::UnsupportedError(ref f) => write!(
322 fmt,
323 "The Decoder does not support the \
324 image format `{f}`"
325 ),
326 TiffError::IoError(ref e) => write!(fmt, "{}", e),
327 TiffError::IntSizeError => write!(fmt, "Platform or format size limits exceeded"),
328 TiffError::UsageError(ref e) => write!(fmt, "Usage error: {e}"),
329 }
330 }
331}
332
333impl Error for TiffError {
334 fn description(&self) -> &str {
335 match *self {
336 TiffError::FormatError(..) => "Format error",
337 TiffError::UnsupportedError(..) => "Unsupported error",
338 TiffError::IoError(..) => "IO error",
339 TiffError::IntSizeError => "Platform or format size limits exceeded",
340 TiffError::UsageError(..) => "Invalid usage",
341 }
342 }
343
344 fn cause(&self) -> Option<&dyn Error> {
345 match *self {
346 TiffError::IoError(ref e) => Some(e),
347 _ => None,
348 }
349 }
350}
351
352impl From<io::Error> for TiffError {
353 fn from(err: io::Error) -> TiffError {
354 TiffError::IoError(err)
355 }
356}
357
358impl From<str::Utf8Error> for TiffError {
359 fn from(_err: str::Utf8Error) -> TiffError {
360 TiffError::FormatError(TiffFormatError::InvalidTag)
361 }
362}
363
364impl From<string::FromUtf8Error> for TiffError {
365 fn from(_err: string::FromUtf8Error) -> TiffError {
366 TiffError::FormatError(TiffFormatError::InvalidTag)
367 }
368}
369
370impl From<TiffFormatError> for TiffError {
371 fn from(err: TiffFormatError) -> TiffError {
372 TiffError::FormatError(err)
373 }
374}
375
376impl From<TiffUnsupportedError> for TiffError {
377 fn from(err: TiffUnsupportedError) -> TiffError {
378 TiffError::UnsupportedError(err)
379 }
380}
381
382impl From<UsageError> for TiffError {
383 fn from(err: UsageError) -> TiffError {
384 TiffError::UsageError(err)
385 }
386}
387
388impl From<std::num::TryFromIntError> for TiffError {
389 fn from(_err: std::num::TryFromIntError) -> TiffError {
390 TiffError::IntSizeError
391 }
392}
393
394pub type TiffResult<T> = Result<T, TiffError>;