acroform_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    // ACROFORM-RS: Removed Jpeg error variant - image decoding removed
138    // #[snafu(display("JPEG Error, caused by\n  {}", source))]
139    // Jpeg {
140    //     #[snafu(source)]
141    //     source: jpeg_decoder::Error
142    // },
143
144    #[snafu(display("IO Error, caused by\n  {}", source))]
145    Io {
146        #[snafu(source)]
147        source: io::Error
148    },
149
150    #[snafu(display("{}", msg))]
151    Other { msg: String },
152
153    #[snafu(display("NoneError at {}:{}:{}:{}", file, line, column, context))]
154    NoneError { file: &'static str, line: u32, column: u32, context: Context },
155
156    #[snafu(display("Try at {}:{}:{}:{}, caused by\n  {}", file, line, column, context, source))]
157    Try {
158        file: &'static str,
159        line: u32,
160        column: u32,
161        context: Context, 
162        #[snafu(source)]
163        source: Box<PdfError>
164    },
165
166    #[snafu(display("PostScriptParseError"))]
167    PostScriptParse,
168
169    #[snafu(display("PostScriptExecError"))]
170    PostScriptExec,
171
172    #[snafu(display("UTF16 decode error"))]
173    Utf16Decode,
174
175    #[snafu(display("UTF8 decode error"))]
176    Utf8Decode,
177
178    #[snafu(display("CID decode error"))]
179    CidDecode,
180
181    #[snafu(display("Max nesting depth reached"))]
182    MaxDepth,
183
184    #[snafu(display("Invalid"))]
185    Invalid,
186}
187impl PdfError {
188    pub fn is_eof(&self) -> bool {
189        match self {
190            PdfError::EOF => true,
191            PdfError::Try { ref source, .. } => source.is_eof(),
192            _ => false
193        }
194    }
195}
196datasize::non_dynamic_const_heap_size!(PdfError, 0);
197
198#[cfg(feature="cache")]
199impl globalcache::ValueSize for PdfError {
200    #[inline]
201    fn size(&self) -> usize {
202        data_size(self)
203    }
204}
205
206#[derive(Debug)]
207pub struct Context(pub Vec<(&'static str, String)>);
208impl std::fmt::Display for Context {
209    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
210        for (i, &(key, ref val)) in self.0.iter().enumerate() {
211            if i == 0 {
212                writeln!(f)?;
213            }
214            writeln!(f, "    {} = {}", key, val)?;
215        }
216        Ok(())
217    }
218}
219
220pub type Result<T, E=PdfError> = std::result::Result<T, E>;
221
222impl From<io::Error> for PdfError {
223    fn from(source: io::Error) -> PdfError {
224        PdfError::Io { source }
225    }
226}
227impl From<String> for PdfError {
228    fn from(msg: String) -> PdfError {
229        PdfError::Other { msg }
230    }
231}
232impl From<Arc<PdfError>> for PdfError {
233    fn from(source: Arc<PdfError>) -> PdfError {
234        PdfError::Shared { source }
235    }
236}
237
238#[macro_export]
239macro_rules! try_opt {
240    ($e:expr $(,$c:expr)*) => (
241        match $e {
242            Some(v) => v,
243            None => {
244                let context = $crate::error::Context(vec![ $( (stringify!($c), format!("{:?}", $c) ) ),* ]);
245                return Err($crate::PdfError::NoneError {
246                    file: file!(),
247                    line: line!(),
248                    column: column!(),
249                    context,
250                });
251            }
252        }
253    );
254}
255
256#[macro_export]
257macro_rules! t {
258    ($e:expr $(,$c:expr)*) => {
259        match $e {
260            Ok(v) => v,
261            Err(e) => {
262                let context = $crate::error::Context(vec![ $( (stringify!($c), format!("{:?}", $c) ) ),* ]);
263                return Err($crate::PdfError::Try { file: file!(), line: line!(), column: column!(), context, source: e.into() })
264            }
265        }
266    };
267}
268
269#[macro_export]
270macro_rules! ctx {
271    ($e:expr, $($c:expr),*) => {
272        match $e {
273            Ok(v) => Ok(v),
274            Err(e) => {
275                let context = $crate::error::Context(vec![ $( (stringify!($c), format!("{:?}", $c) ) ),* ]);
276                Err($crate::PdfError::TryContext { file: file!(), line: line!(), column: column!(), context, source: e.into() })
277            }
278        }
279    };
280}
281
282macro_rules! err_from {
283    ($($st:ty),* => $variant:ident) => (
284        $(
285            impl From<$st> for PdfError {
286                fn from(e: $st) -> PdfError {
287                    PdfError::$variant { source: e.into() }
288                }
289            }
290        )*
291    )
292}
293err_from!(std::str::Utf8Error, std::string::FromUtf8Error, std::string::FromUtf16Error,
294    istring::FromUtf8Error<istring::IBytes>, istring::FromUtf8Error<istring::SmallBytes> => Encoding);
295err_from!(std::num::ParseIntError, std::string::ParseError => Parse);
296// ACROFORM-RS: Removed jpeg_decoder error conversion - image decoding removed
297// err_from!(jpeg_decoder::Error => Jpeg);
298
299macro_rules! other {
300    ($($t:tt)*) => ($crate::PdfError::Other { msg: format!($($t)*) })
301}
302
303macro_rules! err {
304    ($e: expr) => ({
305        return Err($e);
306    })
307}
308macro_rules! bail {
309    ($($t:tt)*) => {
310        err!($crate::PdfError::Other { msg: format!($($t)*) })
311    }
312}
313macro_rules! unimplemented {
314    () => (bail!("Unimplemented @ {}:{}", file!(), line!()))
315}
316
317#[cfg(not(feature = "dump"))]
318pub fn dump_data(_data: &[u8]) {}
319
320#[cfg(feature = "dump")]
321pub fn dump_data(data: &[u8]) {
322    use std::io::Write;
323    if let Some(path) = ::std::env::var_os("PDF_OUT") {
324        let (mut file, path) = tempfile::Builder::new()
325            .prefix("")
326            .tempfile_in(path).unwrap()
327            .keep().unwrap();
328        file.write_all(&data).unwrap();
329        info!("data written to {:?}", path);
330    } else {
331        info!("set PDF_OUT to an existing directory to dump stream data");
332    }
333}
334
335#[cfg(test)]
336mod tests {
337    use super::PdfError;
338
339    fn assert_send<T: Send>() {}
340
341    fn assert_sync<T: Sync>() {}
342
343    #[test]
344    fn error_is_send_and_sync() {
345        // note that these checks happens at compile time, not when the test is run
346        assert_send::<PdfError>();
347        assert_sync::<PdfError>();
348    }
349}