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 #[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, #[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 #[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 #[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 #[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 #[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("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);
296macro_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 assert_send::<PdfError>();
347 assert_sync::<PdfError>();
348 }
349}