pdf/
error.rs

1use crate::object::ObjNr;
2use std::io;
3use std::error::Error;
4use crate::parser::ParseFlags;
5use std::sync::Arc;
6use datasize::{DataSize, data_size};
7use snafu::ErrorCompat;
8
9#[derive(Debug, Snafu)]
10pub enum PdfError {
11    // Syntax / parsing
12    #[snafu(display("Unexpected end of file"))]
13    EOF,
14
15    #[snafu(display("Shared, caused by\n  {}", source))]
16    Shared {
17        #[snafu(source)]
18        source: Arc<PdfError>
19    },
20
21    #[snafu(display("Not enough Operator arguments"))]
22    NoOpArg,
23
24    #[snafu(display("Error parsing from string, caused by\n  {}", source))]
25    Parse {
26        #[snafu(source)]
27        source: Box<dyn Error + Send + Sync>
28    },
29
30    #[snafu(display("Invalid encoding, caused by\n  {}", source))]
31    Encoding { 
32        #[snafu(source)]
33        source: Box<dyn Error + Send + Sync>
34    },
35
36    #[snafu(display("Out of bounds: index {}, but len is {}", index, len))]
37    Bounds { index: usize, len: usize },
38
39    #[snafu(display("Unexpected token '{}' at {} - expected '{}'", lexeme, pos, expected))]
40    UnexpectedLexeme {pos: usize, lexeme: String, expected: &'static str},
41
42    #[snafu(display("Expecting an object, encountered {} at pos {}. Rest:\n{}\n\n((end rest))", first_lexeme, pos, rest))]
43    UnknownType {pos: usize, first_lexeme: String, rest: String},
44
45    #[snafu(display("Unknown variant '{}' for enum {}", name, id))]
46    UnknownVariant { id: &'static str, name: String },
47
48    #[snafu(display("'{}' not found.", word))]
49    NotFound { word: String },
50
51    #[snafu(display("Cannot follow reference during parsing - no resolve fn given (most likely /Length of Stream)."))]
52    Reference, // TODO: which one?
53
54    #[snafu(display("Erroneous 'type' field in xref stream - expected 0, 1 or 2, found {}", found))]
55    XRefStreamType { found: u64 },
56
57    #[snafu(display("Parsing read past boundary of Contents."))]
58    ContentReadPastBoundary,
59
60    #[snafu(display("Primitive not allowed"))]
61    PrimitiveNotAllowed { allowed: ParseFlags, found: ParseFlags },
62
63    //////////////////
64    // Encode/decode
65    #[snafu(display("Hex decode error. Position {}, bytes {:?}", pos, bytes))]
66    HexDecode {pos: usize, bytes: [u8; 2]},
67
68    #[snafu(display("Ascii85 tail error"))]
69    Ascii85TailError,
70
71    #[snafu(display("Failed to convert '{}' into PredictorType", n))]
72    IncorrectPredictorType {n: u8},
73
74    //////////////////
75    // Dictionary
76    #[snafu(display("Can't parse field {} of struct {}, caused by\n  {}", field, typ, source))]
77    FromPrimitive {
78        typ: &'static str,
79        field: &'static str,
80        #[snafu(source)]
81        source: Box<PdfError>
82    },
83
84    #[snafu(display("Field /{} is missing in dictionary for type {}.", field, typ))]
85    MissingEntry {
86        typ: &'static str,
87        field: String
88    },
89
90    #[snafu(display("Expected to find value {} for key {}. Found {} instead.", value, key, found))]
91    KeyValueMismatch {
92        key: String,
93        value: String,
94        found: String,
95    },
96
97    #[snafu(display("Expected dictionary /Type = {}. Found /Type = {}.", expected, found))]
98    WrongDictionaryType {
99        expected: String,
100        found: String
101    },
102
103    //////////////////
104    // Misc
105    #[snafu(display("Tried to dereference free object nr {}.", obj_nr))]
106    FreeObject {obj_nr: u64},
107
108    #[snafu(display("Tried to dereference non-existing object nr {}.", obj_nr))]
109    NullRef {obj_nr: u64},
110
111    #[snafu(display("Expected primitive {}, found primitive {} instead.", expected, found))]
112    UnexpectedPrimitive {expected: &'static str, found: &'static str},
113    /*
114    WrongObjectType {expected: &'static str, found: &'static str} {
115        description("Function called on object of wrong type.")
116        display("Expected {}, found {}.", expected, found)
117    }
118    */
119    #[snafu(display("Object stream index out of bounds ({}/{}).", index, max))]
120    ObjStmOutOfBounds {index: usize, max: usize},
121
122    #[snafu(display("Page out of bounds ({}/{}).", page_nr, max))]
123    PageOutOfBounds {page_nr: u32, max: u32},
124
125    #[snafu(display("Page {} could not be found in the page tree.", page_nr))]
126    PageNotFound {page_nr: u32},
127
128    #[snafu(display("Entry {} in xref table unspecified", id))]
129    UnspecifiedXRefEntry {id: ObjNr},
130
131    #[snafu(display("Invalid password"))]
132    InvalidPassword,
133
134    #[snafu(display("Decryption failure"))]
135    DecryptionFailure,
136
137    #[snafu(display("JPEG Error, caused by\n  {}", source))]
138    Jpeg {
139        #[snafu(source)]
140        source: jpeg_decoder::Error
141    },
142
143    #[snafu(display("IO Error, caused by\n  {}", source))]
144    Io {
145        #[snafu(source)]
146        source: io::Error
147    },
148
149    #[snafu(display("{}", msg))]
150    Other { msg: String },
151
152    #[snafu(display("NoneError at {}:{}:{}:{}", file, line, column, context))]
153    NoneError { file: &'static str, line: u32, column: u32, context: Context },
154
155    #[snafu(display("Try at {}:{}:{}:{}, caused by\n  {}", file, line, column, context, source))]
156    Try {
157        file: &'static str,
158        line: u32,
159        column: u32,
160        context: Context, 
161        #[snafu(source)]
162        source: Box<PdfError>
163    },
164
165    #[snafu(display("PostScriptParseError"))]
166    PostScriptParse,
167
168    #[snafu(display("PostScriptExecError"))]
169    PostScriptExec,
170
171    #[snafu(display("UTF16 decode error"))]
172    Utf16Decode,
173
174    #[snafu(display("UTF8 decode error"))]
175    Utf8Decode,
176
177    #[snafu(display("CID decode error"))]
178    CidDecode,
179
180    #[snafu(display("Max nesting depth reached"))]
181    MaxDepth,
182
183    #[snafu(display("Invalid"))]
184    Invalid,
185}
186impl PdfError {
187    pub fn is_eof(&self) -> bool {
188        match self {
189            PdfError::EOF => true,
190            PdfError::Try { ref source, .. } => source.is_eof(),
191            _ => false
192        }
193    }
194}
195datasize::non_dynamic_const_heap_size!(PdfError, 0);
196
197#[cfg(feature="cache")]
198impl globalcache::ValueSize for PdfError {
199    #[inline]
200    fn size(&self) -> usize {
201        data_size(self)
202    }
203}
204
205#[derive(Debug)]
206pub struct Context(pub Vec<(&'static str, String)>);
207impl std::fmt::Display for Context {
208    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
209        for (i, &(key, ref val)) in self.0.iter().enumerate() {
210            if i == 0 {
211                writeln!(f)?;
212            }
213            writeln!(f, "    {} = {}", key, val)?;
214        }
215        Ok(())
216    }
217}
218
219pub type Result<T, E=PdfError> = std::result::Result<T, E>;
220
221impl From<io::Error> for PdfError {
222    fn from(source: io::Error) -> PdfError {
223        PdfError::Io { source }
224    }
225}
226impl From<String> for PdfError {
227    fn from(msg: String) -> PdfError {
228        PdfError::Other { msg }
229    }
230}
231impl From<Arc<PdfError>> for PdfError {
232    fn from(source: Arc<PdfError>) -> PdfError {
233        PdfError::Shared { source }
234    }
235}
236
237#[macro_export]
238macro_rules! try_opt {
239    ($e:expr $(,$c:expr)*) => (
240        match $e {
241            Some(v) => v,
242            None => {
243                let context = $crate::error::Context(vec![ $( (stringify!($c), format!("{:?}", $c) ) ),* ]);
244                return Err($crate::PdfError::NoneError {
245                    file: file!(),
246                    line: line!(),
247                    column: column!(),
248                    context,
249                });
250            }
251        }
252    );
253}
254
255#[macro_export]
256macro_rules! t {
257    ($e:expr $(,$c:expr)*) => {
258        match $e {
259            Ok(v) => v,
260            Err(e) => {
261                let context = $crate::error::Context(vec![ $( (stringify!($c), format!("{:?}", $c) ) ),* ]);
262                return Err($crate::PdfError::Try { file: file!(), line: line!(), column: column!(), context, source: e.into() })
263            }
264        }
265    };
266}
267
268#[macro_export]
269macro_rules! ctx {
270    ($e:expr, $($c:expr),*) => {
271        match $e {
272            Ok(v) => Ok(v),
273            Err(e) => {
274                let context = $crate::error::Context(vec![ $( (stringify!($c), format!("{:?}", $c) ) ),* ]);
275                Err($crate::PdfError::TryContext { file: file!(), line: line!(), column: column!(), context, source: e.into() })
276            }
277        }
278    };
279}
280
281macro_rules! err_from {
282    ($($st:ty),* => $variant:ident) => (
283        $(
284            impl From<$st> for PdfError {
285                fn from(e: $st) -> PdfError {
286                    PdfError::$variant { source: e.into() }
287                }
288            }
289        )*
290    )
291}
292err_from!(std::str::Utf8Error, std::string::FromUtf8Error, std::string::FromUtf16Error,
293    istring::FromUtf8Error<istring::IBytes>, istring::FromUtf8Error<istring::SmallBytes> => Encoding);
294err_from!(std::num::ParseIntError, std::string::ParseError => Parse);
295err_from!(jpeg_decoder::Error => Jpeg);
296
297macro_rules! other {
298    ($($t:tt)*) => ($crate::PdfError::Other { msg: format!($($t)*) })
299}
300
301macro_rules! err {
302    ($e: expr) => ({
303        return Err($e);
304    })
305}
306macro_rules! bail {
307    ($($t:tt)*) => {
308        err!($crate::PdfError::Other { msg: format!($($t)*) })
309    }
310}
311macro_rules! unimplemented {
312    () => (bail!("Unimplemented @ {}:{}", file!(), line!()))
313}
314
315#[cfg(not(feature = "dump"))]
316pub fn dump_data(_data: &[u8]) {}
317
318#[cfg(feature = "dump")]
319pub fn dump_data(data: &[u8]) {
320    use std::io::Write;
321    if let Some(path) = ::std::env::var_os("PDF_OUT") {
322        let (mut file, path) = tempfile::Builder::new()
323            .prefix("")
324            .tempfile_in(path).unwrap()
325            .keep().unwrap();
326        file.write_all(&data).unwrap();
327        info!("data written to {:?}", path);
328    } else {
329        info!("set PDF_OUT to an existing directory to dump stream data");
330    }
331}
332
333#[cfg(test)]
334mod tests {
335    use super::PdfError;
336
337    fn assert_send<T: Send>() {}
338
339    fn assert_sync<T: Sync>() {}
340
341    #[test]
342    fn error_is_send_and_sync() {
343        // note that these checks happens at compile time, not when the test is run
344        assert_send::<PdfError>();
345        assert_sync::<PdfError>();
346    }
347}