1use crate::object::name::skip_name_like;
4use crate::reader::{Readable, Reader, ReaderContext, Skippable};
5use std::fmt::Debug;
6
7pub use crate::object::array::Array;
8pub use crate::object::dict::Dict;
9pub use crate::object::name::Name;
10pub use crate::object::null::Null;
11pub use crate::object::number::Number;
12pub use crate::object::rect::Rect;
13pub use crate::object::r#ref::{MaybeRef, ObjRef};
14pub use crate::object::stream::Stream;
15pub use crate::object::string::String;
16
17mod bool;
18mod null;
19mod number;
20mod rect;
21mod r#ref;
22mod string;
23mod tuple;
24
25pub(crate) mod indirect;
26pub(crate) mod name;
27
28pub mod array;
29pub mod dict;
30pub mod stream;
31
32pub(crate) trait ObjectLike<'a>: TryFrom<Object<'a>> + Readable<'a> + Debug + Clone {}
34
35#[derive(Debug, Clone, PartialEq)]
37pub enum Object<'a> {
38 Null(Null),
40 Boolean(bool),
42 Number(Number),
44 String(string::String<'a>),
46 Name(Name<'a>),
48 Dict(Dict<'a>),
50 Array(Array<'a>),
52 Stream(Stream<'a>),
56}
57
58impl<'a> Object<'a> {
59 pub(crate) fn cast<T>(self) -> Option<T>
61 where
62 T: ObjectLike<'a>,
63 {
64 self.try_into().ok()
65 }
66
67 #[inline(always)]
69 pub fn into_dict(self) -> Option<Dict<'a>> {
70 self.cast()
71 }
72
73 #[inline(always)]
75 pub fn into_name(self) -> Option<Name<'a>> {
76 self.cast()
77 }
78
79 #[inline(always)]
81 pub fn into_null(self) -> Option<Null> {
82 self.cast()
83 }
84
85 #[inline(always)]
87 pub fn into_bool(self) -> Option<bool> {
88 self.cast()
89 }
90
91 #[inline(always)]
93 pub fn into_string(self) -> Option<string::String<'a>> {
94 self.cast()
95 }
96
97 #[inline(always)]
99 pub fn into_stream(self) -> Option<Stream<'a>> {
100 self.cast()
101 }
102
103 #[inline(always)]
105 pub fn into_array(self) -> Option<Array<'a>> {
106 self.cast()
107 }
108
109 #[inline(always)]
111 pub fn into_u8(self) -> Option<u8> {
112 self.cast()
113 }
114
115 #[inline(always)]
117 pub fn into_u16(self) -> Option<u16> {
118 self.cast()
119 }
120
121 #[inline(always)]
123 pub fn into_f32(self) -> Option<f32> {
124 self.cast()
125 }
126
127 #[inline(always)]
129 pub fn into_i32(self) -> Option<i32> {
130 self.cast()
131 }
132
133 #[inline(always)]
135 pub fn into_number(self) -> Option<Number> {
136 self.cast()
137 }
138}
139
140impl<'a> ObjectLike<'a> for Object<'a> {}
141
142impl Skippable for Object<'_> {
143 fn skip(r: &mut Reader<'_>, is_content_stream: bool) -> Option<()> {
144 match r.peek_byte()? {
145 b'n' => Null::skip(r, is_content_stream),
146 b't' | b'f' => bool::skip(r, is_content_stream),
147 b'/' => Name::skip(r, is_content_stream),
148 b'<' => match r.peek_bytes(2)? {
149 b"<<" => Dict::skip(r, is_content_stream),
151 _ => string::String::skip(r, is_content_stream),
152 },
153 b'(' => string::String::skip(r, is_content_stream),
154 b'.' | b'+' | b'-' | b'0'..=b'9' => Number::skip(r, is_content_stream),
155 b'[' => Array::skip(r, is_content_stream),
156 _ => skip_name_like(r, false),
159 }
160 }
161}
162
163impl<'a> Readable<'a> for Object<'a> {
164 fn read(r: &mut Reader<'a>, ctx: ReaderContext<'a>) -> Option<Self> {
165 let object = match r.peek_byte()? {
166 b'n' => Self::Null(Null::read(r, ctx)?),
167 b't' | b'f' => Self::Boolean(bool::read(r, ctx)?),
168 b'/' => Self::Name(Name::read(r, ctx)?),
169 b'<' => match r.peek_bytes(2)? {
170 b"<<" => {
171 let mut cloned = r.clone();
172 let dict = Dict::read(&mut cloned, ctx)?;
173 cloned.skip_white_spaces_and_comments();
174
175 if cloned.forward_tag(b"stream").is_some() {
176 Object::Stream(Stream::read(r, ctx)?)
177 } else {
178 r.jump(cloned.offset());
179
180 Object::Dict(dict)
181 }
182 }
183 _ => Self::String(string::String::read(r, ctx)?),
184 },
185 b'(' => Self::String(string::String::read(r, ctx)?),
186 b'.' | b'+' | b'-' | b'0'..=b'9' => Self::Number(Number::read(r, ctx)?),
187 b'[' => Self::Array(Array::read(r, ctx)?),
188 _ => {
190 skip_name_like(r, false)?;
191 Self::Null(Null)
192 }
193 };
194
195 Some(object)
196 }
197}
198
199#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
201pub struct ObjectIdentifier {
202 pub(crate) obj_num: i32,
203 pub(crate) gen_num: i32,
204}
205
206impl ObjectIdentifier {
207 pub fn new(obj_num: i32, gen_num: i32) -> Self {
209 Self { obj_num, gen_num }
210 }
211}
212
213impl Readable<'_> for ObjectIdentifier {
214 fn read(r: &mut Reader<'_>, _: ReaderContext) -> Option<Self> {
215 let obj_num = r.read_without_context::<i32>()?;
216 r.skip_white_spaces_and_comments();
217 let gen_num = r.read_without_context::<i32>()?;
218 r.skip_white_spaces_and_comments();
219 r.forward_tag(b"obj")?;
220
221 Some(ObjectIdentifier { obj_num, gen_num })
222 }
223}
224
225impl Skippable for ObjectIdentifier {
226 fn skip(r: &mut Reader<'_>, _: bool) -> Option<()> {
227 r.skip_in_content_stream::<i32>()?;
228 r.skip_white_spaces_and_comments();
229 r.skip_in_content_stream::<i32>()?;
230 r.skip_white_spaces_and_comments();
231 r.forward_tag(b"obj")?;
232
233 Some(())
234 }
235}
236
237pub fn dict_or_stream<'a>(obj: &Object<'a>) -> Option<(Dict<'a>, Option<Stream<'a>>)> {
242 if let Some(stream) = obj.clone().cast::<Stream>() {
243 Some((stream.dict().clone(), Some(stream)))
244 } else {
245 obj.clone().cast::<Dict>().map(|dict| (dict, None))
246 }
247}
248
249mod macros {
250 macro_rules! object {
251 ($t:ident $(<$l:lifetime>),*, $s:ident) => {
252 impl<'a> TryFrom<Object<'a>> for $t$(<$l>),* {
253 type Error = ();
254
255 fn try_from(value: Object<'a>) -> std::result::Result<Self, Self::Error> {
256 match value {
257 Object::$s(b) => Ok(b),
258 _ => Err(()),
259 }
260 }
261 }
262
263 impl<'a> crate::object::ObjectLike<'a> for $t$(<$l>),* {}
264 };
265 }
266
267 pub(crate) use object;
268}
269
270#[cfg(test)]
271mod tests {
272 use crate::object::Object;
273 use crate::reader::{Reader, ReaderContext};
274
275 fn object_impl(data: &[u8]) -> Option<Object> {
276 let mut r = Reader::new(data);
277 r.read_with_context::<Object>(ReaderContext::dummy())
278 }
279
280 #[test]
281 fn null() {
282 assert!(matches!(object_impl(b"null").unwrap(), Object::Null(_)))
283 }
284
285 #[test]
286 fn bool() {
287 assert!(matches!(object_impl(b"true").unwrap(), Object::Boolean(_)))
288 }
289
290 #[test]
291 fn number() {
292 assert!(matches!(object_impl(b"34.5").unwrap(), Object::Number(_)))
293 }
294
295 #[test]
296 fn string_1() {
297 assert!(matches!(object_impl(b"(Hi)").unwrap(), Object::String(_)))
298 }
299
300 #[test]
301 fn string_2() {
302 assert!(matches!(object_impl(b"<34>").unwrap(), Object::String(_)))
303 }
304
305 #[test]
306 fn name() {
307 assert!(matches!(object_impl(b"/Name").unwrap(), Object::Name(_)))
308 }
309
310 #[test]
311 fn dict() {
312 assert!(matches!(
313 object_impl(b"<</Entry 45>>").unwrap(),
314 Object::Dict(_)
315 ))
316 }
317
318 #[test]
319 fn array() {
320 assert!(matches!(object_impl(b"[45]").unwrap(), Object::Array(_)))
321 }
322
323 #[test]
324 fn stream() {
325 assert!(matches!(
326 object_impl(b"<< /Length 3 >> stream\nabc\nendstream").unwrap(),
327 Object::Stream(_)
328 ))
329 }
330}