1pub use crate::object::array::Array;
4pub use crate::object::date::DateTime;
5pub use crate::object::dict::Dict;
6pub use crate::object::name::Name;
7use crate::object::name::skip_name_like;
8pub use crate::object::null::Null;
9pub use crate::object::number::Number;
10pub use crate::object::rect::Rect;
11pub use crate::object::r#ref::{MaybeRef, ObjRef};
12pub use crate::object::stream::Stream;
13pub use crate::object::string::String;
14use crate::reader::Reader;
15use crate::reader::{Readable, ReaderContext, ReaderExt, Skippable};
16use core::fmt::{Debug, Display, Formatter};
17
18const MAX_OBJECT_NESTING_DEPTH: usize = 64;
19
20mod bool;
21mod date;
22mod null;
23mod number;
24mod rect;
25mod r#ref;
26mod string;
27mod tuple;
28
29pub(crate) mod indirect;
30pub(crate) mod name;
31
32pub mod array;
33pub mod dict;
34pub mod stream;
35
36pub(crate) trait ObjectLike<'a>: TryFrom<Object<'a>> + Readable<'a> + Debug + Clone {}
38
39pub(crate) trait ObjectRefLike<'a>: Sized {
41 fn cast_ref<'b>(obj: &'b Object<'a>) -> Option<&'b Self>;
42}
43
44#[derive(Debug, Clone, PartialEq)]
46pub enum Object<'a> {
47 Null(Null),
49 Boolean(bool),
51 Number(Number),
53 String(String<'a>),
55 Name(Name<'a>),
57 Dict(Dict<'a>),
59 Array(Array<'a>),
61 Stream(Stream<'a>),
65}
66
67impl Display for Object<'_> {
68 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
69 match self {
70 Self::Null(value) => Display::fmt(value, f),
71 Self::Boolean(value) => Display::fmt(value, f),
72 Self::Number(value) => Display::fmt(value, f),
73 Self::String(value) => Display::fmt(value, f),
74 Self::Name(value) => Display::fmt(value, f),
75 Self::Dict(value) => Display::fmt(value, f),
76 Self::Array(value) => Display::fmt(value, f),
77 Self::Stream(value) => Display::fmt(value, f),
78 }
79 }
80}
81
82impl<'a> Object<'a> {
83 pub(crate) fn cast<T>(self) -> Option<T>
85 where
86 T: ObjectLike<'a>,
87 {
88 self.try_into().ok()
89 }
90
91 pub(crate) fn cast_ref<T>(&self) -> Option<&T>
93 where
94 T: ObjectRefLike<'a>,
95 {
96 T::cast_ref(self)
97 }
98
99 #[inline(always)]
101 pub fn into_dict(self) -> Option<Dict<'a>> {
102 self.cast()
103 }
104
105 #[inline(always)]
107 pub fn into_name(self) -> Option<Name<'a>> {
108 self.cast()
109 }
110
111 #[inline(always)]
113 pub fn into_null(self) -> Option<Null> {
114 self.cast()
115 }
116
117 #[inline(always)]
119 pub fn into_bool(self) -> Option<bool> {
120 self.cast()
121 }
122
123 #[inline(always)]
125 pub fn into_string(self) -> Option<String<'a>> {
126 self.cast()
127 }
128
129 #[inline(always)]
131 pub fn into_stream(self) -> Option<Stream<'a>> {
132 self.cast()
133 }
134
135 #[inline(always)]
137 pub fn into_array(self) -> Option<Array<'a>> {
138 self.cast()
139 }
140
141 #[inline(always)]
143 pub fn into_u8(self) -> Option<u8> {
144 self.cast()
145 }
146
147 #[inline(always)]
149 pub fn into_u16(self) -> Option<u16> {
150 self.cast()
151 }
152
153 #[inline(always)]
155 pub fn into_f32(self) -> Option<f32> {
156 self.cast()
157 }
158
159 #[inline(always)]
161 pub fn into_i32(self) -> Option<i32> {
162 self.cast()
163 }
164
165 #[inline(always)]
167 pub fn into_number(self) -> Option<Number> {
168 self.cast()
169 }
170}
171
172impl<'a> ObjectLike<'a> for Object<'a> {}
173impl<'a> ObjectRefLike<'a> for Object<'a> {
174 fn cast_ref(obj: &Self) -> Option<&Self> {
175 Some(obj)
176 }
177}
178
179impl Skippable for Object<'_> {
180 fn skip(r: &mut Reader<'_>, is_content_stream: bool) -> Option<()> {
181 Self::skip_with_depth(r, is_content_stream, 0)
182 }
183
184 fn skip_with_depth(r: &mut Reader<'_>, is_content_stream: bool, depth: usize) -> Option<()> {
185 match r.peek_byte()? {
186 b'n' => Null::skip(r, is_content_stream),
187 b't' | b'f' => bool::skip(r, is_content_stream),
188 b'/' => Name::skip(r, is_content_stream),
189 b'<' => match r.peek_bytes(2)? {
190 b"<<" => Dict::skip_with_depth(r, is_content_stream, depth),
192 _ => String::skip(r, is_content_stream),
193 },
194 b'(' => String::skip(r, is_content_stream),
195 b'.' | b'+' | b'-' | b'0'..=b'9' => Number::skip(r, is_content_stream),
196 b'[' => Array::skip_with_depth(r, is_content_stream, depth),
197 _ => skip_name_like(r, false),
200 }
201 }
202}
203
204impl<'a> Readable<'a> for Object<'a> {
205 fn read(r: &mut Reader<'a>, ctx: &ReaderContext<'a>) -> Option<Self> {
206 let object = match r.peek_byte()? {
207 b'n' => Self::Null(Null::read(r, ctx)?),
208 b't' | b'f' => Self::Boolean(bool::read(r, ctx)?),
209 b'/' => Self::Name(Name::read(r, ctx)?),
210 b'<' => match r.peek_bytes(2)? {
211 b"<<" => {
212 let mut cloned = r.clone();
213 let dict = Dict::read(&mut cloned, ctx)?;
214 cloned.skip_white_spaces_and_comments();
215
216 if cloned.forward_tag(b"stream").is_some() {
217 Object::Stream(Stream::read(r, ctx)?)
218 } else {
219 r.jump(cloned.offset());
220
221 Object::Dict(dict)
222 }
223 }
224 _ => Self::String(String::read(r, ctx)?),
225 },
226 b'(' => Self::String(String::read(r, ctx)?),
227 b'.' | b'+' | b'-' | b'0'..=b'9' => Self::Number(Number::read(r, ctx)?),
228 b'[' => Self::Array(Array::read(r, ctx)?),
229 _ => {
231 skip_name_like(r, false)?;
232 Self::Null(Null)
233 }
234 };
235
236 Some(object)
237 }
238}
239
240pub trait FromBytes<'a>: Sized {
242 fn from_bytes(b: &'a [u8]) -> Option<Self>;
244}
245
246impl<'a, T: Readable<'a>> FromBytes<'a> for T {
247 fn from_bytes(b: &'a [u8]) -> Option<Self> {
248 Self::from_bytes_impl(b)
249 }
250}
251
252#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
254pub struct ObjectIdentifier {
255 pub obj_number: i32,
257 pub gen_number: i32,
259}
260
261impl ObjectIdentifier {
262 pub fn new(obj_number: i32, gen_number: i32) -> Self {
264 Self {
265 obj_number,
266 gen_number,
267 }
268 }
269}
270
271impl Readable<'_> for ObjectIdentifier {
272 fn read(r: &mut Reader<'_>, _: &ReaderContext<'_>) -> Option<Self> {
273 let obj_number = r.read_without_context::<i32>()?;
274 r.skip_white_spaces_and_comments();
275 let gen_number = r.read_without_context::<i32>()?;
276 r.skip_white_spaces_and_comments();
277 r.forward_tag(b"obj")?;
278
279 Some(Self {
280 obj_number,
281 gen_number,
282 })
283 }
284}
285
286impl Skippable for ObjectIdentifier {
287 fn skip(r: &mut Reader<'_>, _: bool) -> Option<()> {
288 r.skip::<i32>(false)?;
289 r.skip_white_spaces_and_comments();
290 r.skip::<i32>(false)?;
291 r.skip_white_spaces_and_comments();
292 r.forward_tag(b"obj")?;
293
294 Some(())
295 }
296}
297
298pub fn dict_or_stream<'a, 'b>(
303 obj: &'b Object<'a>,
304) -> Option<(&'b Dict<'a>, Option<&'b Stream<'a>>)> {
305 if let Some(stream) = obj.cast_ref::<Stream<'a>>() {
306 Some((stream.dict(), Some(stream)))
307 } else {
308 obj.cast_ref::<Dict<'a>>().map(|dict| (dict, None))
309 }
310}
311
312mod macros {
313 macro_rules! object {
314 ($t:ident $(<$l:lifetime>),*, $s:ident) => {
315 impl<'a> TryFrom<Object<'a>> for $t$(<$l>),* {
316 type Error = ();
317
318 fn try_from(value: Object<'a>) -> core::result::Result<Self, Self::Error> {
319 match value {
320 Object::$s(b) => Ok(b),
321 _ => Err(()),
322 }
323 }
324 }
325
326 impl<'a> crate::object::ObjectLike<'a> for $t$(<$l>),* {}
327
328 impl<'a> crate::object::ObjectRefLike<'a> for $t$(<$l>),* {
329 fn cast_ref<'b>(obj: &'b crate::object::Object<'a>) -> Option<&'b Self> {
330 match obj {
331 crate::object::Object::$s(b) => Some(b),
332 _ => None,
333 }
334 }
335 }
336 };
337 }
338
339 pub(crate) use object;
340}
341
342#[cfg(test)]
343mod tests {
344 use crate::object::{Array, Dict, Name, Null, Number, Object, Stream, String};
345 use crate::reader::Reader;
346 use crate::reader::{ReaderContext, ReaderExt};
347
348 fn object_impl(data: &[u8]) -> Option<Object<'_>> {
349 let mut r = Reader::new(data);
350 r.read_with_context::<Object<'_>>(&ReaderContext::dummy())
351 }
352
353 #[test]
354 fn deeply_nested_arrays() {
355 let data = format!("{}0{}", "[".repeat(5000), "]".repeat(5000));
356 assert!(object_impl(data.as_bytes()).is_none());
357 }
358
359 #[test]
360 fn deeply_nested_dictionaries() {
361 let data = format!("{}0{}", "<< /X ".repeat(5000), " >>".repeat(5000));
362 assert!(object_impl(data.as_bytes()).is_none());
363 }
364
365 #[test]
366 fn null() {
367 assert!(matches!(object_impl(b"null").unwrap(), Object::Null(_)));
368 }
369
370 #[test]
371 fn bool() {
372 assert!(matches!(object_impl(b"true").unwrap(), Object::Boolean(_)));
373 }
374
375 #[test]
376 fn number() {
377 assert!(matches!(object_impl(b"34.5").unwrap(), Object::Number(_)));
378 }
379
380 #[test]
381 fn string_1() {
382 assert!(matches!(object_impl(b"(Hi)").unwrap(), Object::String(_)));
383 }
384
385 #[test]
386 fn string_2() {
387 assert!(matches!(object_impl(b"<34>").unwrap(), Object::String(_)));
388 }
389
390 #[test]
391 fn name() {
392 assert!(matches!(object_impl(b"/Name").unwrap(), Object::Name(_)));
393 }
394
395 #[test]
396 fn dict() {
397 assert!(matches!(
398 object_impl(b"<</Entry 45>>").unwrap(),
399 Object::Dict(_)
400 ));
401 }
402
403 #[test]
404 fn array() {
405 assert!(matches!(object_impl(b"[45]").unwrap(), Object::Array(_)));
406 }
407
408 #[test]
409 fn stream() {
410 assert!(matches!(
411 object_impl(b"<< /Length 3 >> stream\nabc\nendstream").unwrap(),
412 Object::Stream(_)
413 ));
414 }
415
416 #[test]
417 fn display() {
418 let cases: &[(&[u8], &str)] = &[
419 (b"null", "null"),
420 (b"true", "true"),
421 (b"34.5", "34.5"),
422 (b"(Hi)", "\"Hi\""),
423 (b"/Name", "/Name"),
424 (b"[45]", "[45]"),
425 (b"<</Entry 45>>", "<< /Entry 45 >>"),
426 (b"<< /Length 3 >> stream\nabc\nendstream", "Stream (len: 3)"),
427 ];
428
429 for (input, expected) in cases {
430 assert_eq!(format!("{}", object_impl(input).unwrap()), *expected);
431 }
432 }
433
434 #[test]
435 #[cfg(target_pointer_width = "64")]
436 fn object_sizes() {
437 assert_eq!(size_of::<Object<'_>>(), 40);
438 assert_eq!(size_of::<Array<'_>>(), 32);
439 assert_eq!(size_of::<Dict<'_>>(), 8);
440 assert_eq!(size_of::<Name<'_>>(), 32);
441 assert_eq!(size_of::<Null>(), 0);
442 assert_eq!(size_of::<Number>(), 16);
443 assert_eq!(size_of::<Stream<'_>>(), 24);
444 assert_eq!(size_of::<String<'_>>(), 32);
445 }
446}