1use std::fmt;
8
9#[derive(Clone, PartialEq)]
11pub enum PdfObj {
12 Null,
13 Bool(bool),
14 Int(i64),
15 Real(f64),
16 Name(Vec<u8>),
18 Str(Vec<u8>),
20 Array(Vec<PdfObj>),
21 Dict(PdfDict),
22 Stream {
24 dict: PdfDict,
25 data_offset: usize,
26 data_len: usize,
27 },
28 Ref(u32, u16),
30}
31
32impl PdfObj {
33 pub fn as_int(&self) -> Option<i64> {
35 match self {
36 PdfObj::Int(n) => Some(*n),
37 _ => None,
38 }
39 }
40
41 pub fn as_f64(&self) -> Option<f64> {
43 match self {
44 PdfObj::Int(n) => Some(*n as f64),
45 PdfObj::Real(f) => Some(*f),
46 _ => None,
47 }
48 }
49
50 pub fn as_name(&self) -> Option<&[u8]> {
52 match self {
53 PdfObj::Name(n) => Some(n),
54 _ => None,
55 }
56 }
57
58 pub fn as_str(&self) -> Option<&[u8]> {
60 match self {
61 PdfObj::Str(s) => Some(s),
62 _ => None,
63 }
64 }
65
66 pub fn as_array(&self) -> Option<&[PdfObj]> {
68 match self {
69 PdfObj::Array(a) => Some(a),
70 _ => None,
71 }
72 }
73
74 pub fn as_dict(&self) -> Option<&PdfDict> {
76 match self {
77 PdfObj::Dict(d) => Some(d),
78 PdfObj::Stream { dict, .. } => Some(dict),
79 _ => None,
80 }
81 }
82
83 pub fn as_ref(&self) -> Option<(u32, u16)> {
85 match self {
86 PdfObj::Ref(n, g) => Some((*n, *g)),
87 _ => None,
88 }
89 }
90
91 pub fn to_ref(&self) -> Option<(u32, u16)> {
94 self.as_ref()
95 }
96}
97
98impl fmt::Debug for PdfObj {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 match self {
101 PdfObj::Null => write!(f, "null"),
102 PdfObj::Bool(b) => write!(f, "{b}"),
103 PdfObj::Int(n) => write!(f, "{n}"),
104 PdfObj::Real(r) => write!(f, "{r}"),
105 PdfObj::Name(n) => write!(f, "/{}", String::from_utf8_lossy(n)),
106 PdfObj::Str(s) => write!(f, "({:?})", String::from_utf8_lossy(s)),
107 PdfObj::Array(a) => f.debug_list().entries(a.iter()).finish(),
108 PdfObj::Dict(d) => write!(f, "{d:?}"),
109 PdfObj::Stream { dict, data_len, .. } => {
110 write!(f, "{dict:?} stream({data_len} bytes)")
111 }
112 PdfObj::Ref(n, g) => write!(f, "{n} {g} R"),
113 }
114 }
115}
116
117#[derive(Clone, PartialEq, Default)]
119pub struct PdfDict(Vec<(Vec<u8>, PdfObj)>);
120
121impl PdfDict {
122 pub fn new() -> Self {
123 Self(Vec::new())
124 }
125
126 pub fn get(&self, key: &[u8]) -> Option<&PdfObj> {
128 self.0.iter().rev().find(|(k, _)| k == key).map(|(_, v)| v)
129 }
130
131 pub fn get_name(&self, key: &[u8]) -> Option<&[u8]> {
133 self.get(key).and_then(|v| v.as_name())
134 }
135
136 pub fn get_int(&self, key: &[u8]) -> Option<i64> {
138 self.get(key).and_then(|v| v.as_int())
139 }
140
141 pub fn get_f64(&self, key: &[u8]) -> Option<f64> {
143 self.get(key).and_then(|v| v.as_f64())
144 }
145
146 pub fn get_array(&self, key: &[u8]) -> Option<&[PdfObj]> {
148 self.get(key).and_then(|v| v.as_array())
149 }
150
151 pub fn get_dict(&self, key: &[u8]) -> Option<&PdfDict> {
153 self.get(key).and_then(|v| v.as_dict())
154 }
155
156 pub fn get_ref(&self, key: &[u8]) -> Option<(u32, u16)> {
158 self.get(key).and_then(|v| v.as_ref())
159 }
160
161 pub fn get_derefable(&self, key: &[u8]) -> Option<&PdfObj> {
163 self.get(key)
164 }
165
166 pub fn insert(&mut self, key: Vec<u8>, val: PdfObj) {
168 if self.0.len() < 100 {
172 if let Some(entry) = self.0.iter_mut().find(|(k, _)| k == &key) {
173 entry.1 = val;
174 return;
175 }
176 }
177 self.0.push((key, val));
178 }
179
180 pub fn entries(&self) -> &[(Vec<u8>, PdfObj)] {
182 &self.0
183 }
184
185 pub fn into_entries(self) -> Vec<(Vec<u8>, PdfObj)> {
187 self.0
188 }
189
190 pub fn from_entries(entries: Vec<(Vec<u8>, PdfObj)>) -> Self {
192 PdfDict(entries)
193 }
194
195 pub fn len(&self) -> usize {
197 self.0.len()
198 }
199
200 pub fn is_empty(&self) -> bool {
202 self.0.is_empty()
203 }
204}
205
206impl fmt::Debug for PdfDict {
207 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208 write!(f, "<<")?;
209 for (k, v) in &self.0 {
210 write!(f, " /{} {v:?}", String::from_utf8_lossy(k))?;
211 }
212 write!(f, " >>")
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219
220 #[test]
221 fn dict_get_and_insert() {
222 let mut d = PdfDict::new();
223 d.insert(b"Type".to_vec(), PdfObj::Name(b"Page".to_vec()));
224 d.insert(b"Count".to_vec(), PdfObj::Int(5));
225
226 assert_eq!(d.get_name(b"Type"), Some(b"Page".as_slice()));
227 assert_eq!(d.get_int(b"Count"), Some(5));
228 assert_eq!(d.get(b"Missing"), None);
229 assert_eq!(d.len(), 2);
230 }
231
232 #[test]
233 fn dict_insert_replaces() {
234 let mut d = PdfDict::new();
235 d.insert(b"Key".to_vec(), PdfObj::Int(1));
236 d.insert(b"Key".to_vec(), PdfObj::Int(2));
237 assert_eq!(d.get_int(b"Key"), Some(2));
238 assert_eq!(d.len(), 1);
239 }
240
241 #[test]
242 fn obj_as_f64_int_and_real() {
243 assert_eq!(PdfObj::Int(42).as_f64(), Some(42.0));
244 assert_eq!(PdfObj::Real(2.5).as_f64(), Some(2.5));
245 assert_eq!(PdfObj::Null.as_f64(), None);
246 }
247
248 #[test]
249 fn obj_as_dict_from_stream() {
250 let mut d = PdfDict::new();
251 d.insert(b"Length".to_vec(), PdfObj::Int(100));
252 let obj = PdfObj::Stream {
253 dict: d.clone(),
254 data_offset: 0,
255 data_len: 100,
256 };
257 assert!(obj.as_dict().is_some());
258 assert_eq!(obj.as_dict().unwrap().get_int(b"Length"), Some(100));
259 }
260}