Skip to main content

oxidize_pdf/objects/
primitive.rs

1use crate::objects::Dictionary;
2use std::fmt;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub struct ObjectId {
6    number: u32,
7    generation: u16,
8}
9
10impl ObjectId {
11    pub fn new(number: u32, generation: u16) -> Self {
12        Self { number, generation }
13    }
14
15    pub fn number(&self) -> u32 {
16        self.number
17    }
18
19    pub fn generation(&self) -> u16 {
20        self.generation
21    }
22}
23
24impl fmt::Display for ObjectId {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        write!(f, "{} {} R", self.number, self.generation)
27    }
28}
29
30#[derive(Debug, Clone, PartialEq)]
31pub enum Object {
32    Null,
33    Boolean(bool),
34    Integer(i64),
35    Real(f64),
36    String(String),
37    Name(String),
38    Array(Vec<Object>),
39    Dictionary(Dictionary),
40    Stream(Dictionary, Vec<u8>),
41    Reference(ObjectId),
42}
43
44impl Object {
45    pub fn is_null(&self) -> bool {
46        matches!(self, Object::Null)
47    }
48
49    pub fn as_bool(&self) -> Option<bool> {
50        match self {
51            Object::Boolean(b) => Some(*b),
52            _ => None,
53        }
54    }
55
56    pub fn as_integer(&self) -> Option<i64> {
57        match self {
58            Object::Integer(i) => Some(*i),
59            _ => None,
60        }
61    }
62
63    pub fn as_real(&self) -> Option<f64> {
64        match self {
65            Object::Real(f) => Some(*f),
66            Object::Integer(i) => Some(*i as f64),
67            _ => None,
68        }
69    }
70
71    pub fn as_string(&self) -> Option<&str> {
72        match self {
73            Object::String(s) => Some(s),
74            _ => None,
75        }
76    }
77
78    pub fn as_name(&self) -> Option<&str> {
79        match self {
80            Object::Name(n) => Some(n),
81            _ => None,
82        }
83    }
84
85    pub fn as_array(&self) -> Option<&Vec<Object>> {
86        match self {
87            Object::Array(arr) => Some(arr),
88            _ => None,
89        }
90    }
91
92    pub fn as_dict(&self) -> Option<&Dictionary> {
93        match self {
94            Object::Dictionary(dict) => Some(dict),
95            _ => None,
96        }
97    }
98}
99
100impl From<bool> for Object {
101    fn from(b: bool) -> Self {
102        Object::Boolean(b)
103    }
104}
105
106impl From<i32> for Object {
107    fn from(i: i32) -> Self {
108        Object::Integer(i as i64)
109    }
110}
111
112impl From<i64> for Object {
113    fn from(i: i64) -> Self {
114        Object::Integer(i)
115    }
116}
117
118impl From<f32> for Object {
119    fn from(f: f32) -> Self {
120        Object::Real(f as f64)
121    }
122}
123
124impl From<f64> for Object {
125    fn from(f: f64) -> Self {
126        Object::Real(f)
127    }
128}
129
130impl From<String> for Object {
131    fn from(s: String) -> Self {
132        Object::String(s)
133    }
134}
135
136impl From<&str> for Object {
137    fn from(s: &str) -> Self {
138        Object::String(s.to_string())
139    }
140}
141
142impl From<Vec<Object>> for Object {
143    fn from(v: Vec<Object>) -> Self {
144        Object::Array(v)
145    }
146}
147
148impl From<Dictionary> for Object {
149    fn from(d: Dictionary) -> Self {
150        Object::Dictionary(d)
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157
158    #[test]
159    fn test_object_id_new() {
160        let id = ObjectId::new(42, 5);
161        assert_eq!(id.number(), 42);
162        assert_eq!(id.generation(), 5);
163    }
164
165    #[test]
166    fn test_object_id_display() {
167        let id = ObjectId::new(10, 0);
168        assert_eq!(format!("{id}"), "10 0 R");
169
170        let id2 = ObjectId::new(999, 65535);
171        assert_eq!(format!("{id2}"), "999 65535 R");
172    }
173
174    #[test]
175    fn test_object_id_equality() {
176        let id1 = ObjectId::new(1, 0);
177        let id2 = ObjectId::new(1, 0);
178        let id3 = ObjectId::new(2, 0);
179        let id4 = ObjectId::new(1, 1);
180
181        assert_eq!(id1, id2);
182        assert_ne!(id1, id3);
183        assert_ne!(id1, id4);
184    }
185
186    #[test]
187    fn test_object_null() {
188        let obj = Object::Null;
189        assert!(obj.is_null());
190        assert!(obj.as_bool().is_none());
191        assert!(obj.as_integer().is_none());
192        assert!(obj.as_real().is_none());
193        assert!(obj.as_string().is_none());
194        assert!(obj.as_name().is_none());
195        assert!(obj.as_array().is_none());
196        assert!(obj.as_dict().is_none());
197    }
198
199    #[test]
200    fn test_object_boolean() {
201        let obj_true = Object::Boolean(true);
202        let obj_false = Object::Boolean(false);
203
204        assert!(!obj_true.is_null());
205        assert_eq!(obj_true.as_bool(), Some(true));
206        assert_eq!(obj_false.as_bool(), Some(false));
207        assert!(obj_true.as_integer().is_none());
208    }
209
210    #[test]
211    fn test_object_integer() {
212        let obj = Object::Integer(42);
213
214        assert_eq!(obj.as_integer(), Some(42));
215        assert_eq!(obj.as_real(), Some(42.0));
216        assert!(obj.as_bool().is_none());
217        assert!(obj.as_string().is_none());
218    }
219
220    #[test]
221    fn test_object_real() {
222        let obj = Object::Real(3.14159);
223
224        assert_eq!(obj.as_real(), Some(3.14159));
225        assert!(obj.as_integer().is_none());
226        assert!(obj.as_bool().is_none());
227    }
228
229    #[test]
230    fn test_object_string() {
231        let obj = Object::String("Hello PDF".to_string());
232
233        assert_eq!(obj.as_string(), Some("Hello PDF"));
234        assert!(obj.as_name().is_none());
235        assert!(obj.as_integer().is_none());
236    }
237
238    #[test]
239    fn test_object_name() {
240        let obj = Object::Name("Type".to_string());
241
242        assert_eq!(obj.as_name(), Some("Type"));
243        assert!(obj.as_string().is_none());
244        assert!(obj.as_integer().is_none());
245    }
246
247    #[test]
248    fn test_object_array() {
249        let arr = vec![Object::Integer(1), Object::Integer(2), Object::Integer(3)];
250        let obj = Object::Array(arr.clone());
251
252        assert_eq!(obj.as_array(), Some(&arr));
253        assert!(obj.as_dict().is_none());
254    }
255
256    #[test]
257    fn test_object_dictionary() {
258        let mut dict = Dictionary::new();
259        dict.set("Key", "Value");
260        let obj = Object::Dictionary(dict.clone());
261
262        assert_eq!(obj.as_dict(), Some(&dict));
263        assert!(obj.as_array().is_none());
264    }
265
266    #[test]
267    fn test_object_stream() {
268        let mut dict = Dictionary::new();
269        dict.set("Length", 5);
270        let data = vec![1, 2, 3, 4, 5];
271        let obj = Object::Stream(dict, data);
272
273        // Stream doesn't have as_stream method, but we can pattern match
274        if let Object::Stream(d, data) = obj {
275            assert_eq!(d.get("Length"), Some(&Object::Integer(5)));
276            assert_eq!(data.len(), 5);
277        } else {
278            panic!("Expected Stream object");
279        }
280    }
281
282    #[test]
283    fn test_object_reference() {
284        let id = ObjectId::new(10, 0);
285        let obj = Object::Reference(id);
286
287        // Reference doesn't have as_reference method, but we can pattern match
288        if let Object::Reference(ref_id) = obj {
289            assert_eq!(ref_id, id);
290        } else {
291            panic!("Expected Reference object");
292        }
293    }
294
295    #[test]
296    fn test_from_bool() {
297        let obj: Object = true.into();
298        assert_eq!(obj, Object::Boolean(true));
299
300        let obj2: Object = false.into();
301        assert_eq!(obj2, Object::Boolean(false));
302    }
303
304    #[test]
305    fn test_from_integers() {
306        let obj: Object = 42i32.into();
307        assert_eq!(obj, Object::Integer(42));
308
309        let obj2: Object = 9999i64.into();
310        assert_eq!(obj2, Object::Integer(9999));
311
312        let obj3: Object = (-100i32).into();
313        assert_eq!(obj3, Object::Integer(-100));
314    }
315
316    #[test]
317    fn test_from_floats() {
318        let obj: Object = std::f32::consts::PI.into();
319        if let Object::Real(val) = obj {
320            assert!((val - std::f64::consts::PI).abs() < 0.001);
321        } else {
322            panic!("Expected Real object");
323        }
324
325        let obj2: Object = 2.71828f64.into();
326        assert_eq!(obj2, Object::Real(2.71828));
327    }
328
329    #[test]
330    fn test_from_strings() {
331        let obj: Object = "Hello".into();
332        assert_eq!(obj, Object::String("Hello".to_string()));
333
334        let obj2: Object = String::from("World").into();
335        assert_eq!(obj2, Object::String("World".to_string()));
336    }
337
338    #[test]
339    fn test_from_vec() {
340        let vec = vec![Object::Integer(1), Object::Integer(2)];
341        let obj: Object = vec.clone().into();
342        assert_eq!(obj, Object::Array(vec));
343    }
344
345    #[test]
346    fn test_from_dictionary() {
347        let mut dict = Dictionary::new();
348        dict.set("Test", 123);
349        let obj: Object = dict.clone().into();
350
351        if let Object::Dictionary(d) = obj {
352            assert_eq!(d.get("Test"), Some(&Object::Integer(123)));
353        } else {
354            panic!("Expected Dictionary object");
355        }
356    }
357
358    #[test]
359    fn test_object_equality() {
360        assert_eq!(Object::Null, Object::Null);
361        assert_eq!(Object::Boolean(true), Object::Boolean(true));
362        assert_ne!(Object::Boolean(true), Object::Boolean(false));
363        assert_eq!(Object::Integer(42), Object::Integer(42));
364        assert_ne!(Object::Integer(42), Object::Integer(43));
365        assert_eq!(
366            Object::String("A".to_string()),
367            Object::String("A".to_string())
368        );
369        assert_ne!(
370            Object::String("A".to_string()),
371            Object::String("B".to_string())
372        );
373    }
374}