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("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 assert_send::<PdfError>();
345 assert_sync::<PdfError>();
346 }
347}