Skip to main content

pdf/
error.rs

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