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 #[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, #[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 #[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 #[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 #[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 #[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 assert_send::<PdfError>();
398 assert_sync::<PdfError>();
399 }
400}