1use std::collections::HashMap;
9
10pub use rpdfium_core::error::ObjectId;
11use rpdfium_core::{Name, PdfString};
12
13#[derive(Debug, Clone)]
18pub enum StreamData {
19 Raw { offset: u64, length: u64 },
21 Decoded { data: Vec<u8> },
23}
24
25#[derive(Debug, Clone)]
30pub enum Object {
31 Null,
33 Boolean(bool),
35 Integer(i64),
37 Real(f64),
39 String(PdfString),
41 Name(Name),
43 Array(Vec<Object>),
45 Dictionary(HashMap<Name, Object>),
47 Stream {
49 dict: HashMap<Name, Object>,
50 data: StreamData,
51 },
52 Reference(ObjectId),
54}
55
56impl Object {
57 pub fn as_bool(&self) -> Option<bool> {
59 match self {
60 Object::Boolean(b) => Some(*b),
61 _ => None,
62 }
63 }
64
65 pub fn as_i64(&self) -> Option<i64> {
67 match self {
68 Object::Integer(n) => Some(*n),
69 _ => None,
70 }
71 }
72
73 pub fn as_f64(&self) -> Option<f64> {
75 match self {
76 Object::Real(f) => Some(*f),
77 Object::Integer(n) => Some(*n as f64),
78 _ => None,
79 }
80 }
81
82 pub fn as_string(&self) -> Option<&PdfString> {
84 match self {
85 Object::String(s) => Some(s),
86 _ => None,
87 }
88 }
89
90 pub fn as_name(&self) -> Option<&Name> {
92 match self {
93 Object::Name(n) => Some(n),
94 _ => None,
95 }
96 }
97
98 pub fn as_array(&self) -> Option<&[Object]> {
100 match self {
101 Object::Array(a) => Some(a),
102 _ => None,
103 }
104 }
105
106 pub fn as_dict(&self) -> Option<&HashMap<Name, Object>> {
108 match self {
109 Object::Dictionary(d) => Some(d),
110 _ => None,
111 }
112 }
113
114 pub fn as_stream_dict(&self) -> Option<&HashMap<Name, Object>> {
116 match self {
117 Object::Stream { dict, .. } => Some(dict),
118 _ => None,
119 }
120 }
121
122 pub fn as_reference(&self) -> Option<ObjectId> {
124 match self {
125 Object::Reference(id) => Some(*id),
126 _ => None,
127 }
128 }
129
130 pub fn as_dict_mut(&mut self) -> Option<&mut HashMap<Name, Object>> {
132 match self {
133 Object::Dictionary(d) => Some(d),
134 _ => None,
135 }
136 }
137
138 pub fn as_array_mut(&mut self) -> Option<&mut Vec<Object>> {
140 match self {
141 Object::Array(a) => Some(a),
142 _ => None,
143 }
144 }
145
146 pub fn is_null(&self) -> bool {
148 matches!(self, Object::Null)
149 }
150
151 pub fn is_reference(&self) -> bool {
153 matches!(self, Object::Reference(_))
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn object_id_display_format() {
163 let id = ObjectId::new(42, 0);
164 assert_eq!(format!("{}", id), "42 0 R");
165 }
166
167 #[test]
168 fn object_id_equality() {
169 let a = ObjectId::new(1, 0);
170 let b = ObjectId::new(1, 0);
171 let c = ObjectId::new(1, 1);
172 assert_eq!(a, b);
173 assert_ne!(a, c);
174 }
175
176 #[test]
177 fn object_null() {
178 let obj = Object::Null;
179 assert!(obj.is_null());
180 assert_eq!(obj.as_bool(), None);
181 assert_eq!(obj.as_i64(), None);
182 }
183
184 #[test]
185 fn object_boolean() {
186 let obj = Object::Boolean(true);
187 assert_eq!(obj.as_bool(), Some(true));
188 assert_eq!(obj.as_i64(), None);
189 }
190
191 #[test]
192 fn object_integer() {
193 let obj = Object::Integer(42);
194 assert_eq!(obj.as_i64(), Some(42));
195 assert_eq!(obj.as_f64(), Some(42.0));
196 assert_eq!(obj.as_bool(), None);
197 }
198
199 #[test]
200 #[allow(clippy::approx_constant)]
201 fn object_real() {
202 let obj = Object::Real(3.14);
203 assert_eq!(obj.as_f64(), Some(3.14));
204 assert_eq!(obj.as_i64(), None);
205 }
206
207 #[test]
208 fn object_string() {
209 let obj = Object::String(PdfString::from_bytes(b"hello".to_vec()));
210 assert!(obj.as_string().is_some());
211 assert_eq!(obj.as_string().unwrap().as_bytes(), b"hello");
212 }
213
214 #[test]
215 fn object_name() {
216 let obj = Object::Name(Name::from_bytes(b"Type".to_vec()));
217 assert!(obj.as_name().is_some());
218 }
219
220 #[test]
221 fn object_array() {
222 let obj = Object::Array(vec![Object::Integer(1), Object::Integer(2)]);
223 assert_eq!(obj.as_array().unwrap().len(), 2);
224 }
225
226 #[test]
227 fn object_dictionary() {
228 let mut dict = HashMap::new();
229 dict.insert(
230 Name::r#type(),
231 Object::Name(Name::from_bytes(b"Catalog".to_vec())),
232 );
233 let obj = Object::Dictionary(dict);
234 assert!(obj.as_dict().is_some());
235 }
236
237 #[test]
238 fn object_stream() {
239 let dict = HashMap::new();
240 let data = StreamData::Raw {
241 offset: 0,
242 length: 100,
243 };
244 let obj = Object::Stream { dict, data };
245 assert!(obj.as_stream_dict().is_some());
246 }
247
248 #[test]
249 fn object_reference() {
250 let id = ObjectId::new(5, 0);
251 let obj = Object::Reference(id);
252 assert!(obj.is_reference());
253 assert_eq!(obj.as_reference(), Some(id));
254 }
255}