1use std::collections::BTreeMap;
7use std::sync::Arc;
8
9use otf_pdf_syntax::filters::Decoded;
10use otf_pdf_syntax::xref::XrefEntry;
11use otf_pdf_syntax::{
12 Diagnostics, Dict, Error, File, LimitKind, Limits, ObjId, Object, Result, Stream, Strictness,
13};
14
15#[derive(Debug, Clone)]
17pub(crate) enum Change {
18 Set(Arc<Object>),
20 Deleted,
22}
23
24#[derive(Debug)]
26pub struct Store {
27 file: File,
28 overlay: BTreeMap<ObjId, Change>,
29 trailer: Dict,
30 next_num: u32,
31}
32
33impl Store {
34 pub(crate) fn new(file: File) -> Self {
35 let trailer = file.trailer().clone();
36 let highest = file.entries().keys().next_back().copied().unwrap_or(0);
37 let size = trailer
38 .get_int(b"Size")
39 .and_then(|s| u32::try_from(s).ok())
40 .unwrap_or(0);
41 Self {
42 file,
43 overlay: BTreeMap::new(),
44 trailer,
45 next_num: highest.saturating_add(1).max(size).max(1),
46 }
47 }
48
49 pub fn file(&self) -> &File {
51 &self.file
52 }
53
54 pub fn trailer(&self) -> &Dict {
56 &self.trailer
57 }
58
59 pub fn set_trailer_entry(&mut self, key: &str, value: Object) {
61 if value == Object::Null {
62 self.trailer.remove(key.as_bytes());
63 } else {
64 self.trailer.insert(key, value);
65 }
66 }
67
68 pub(crate) fn overlay(&self) -> &BTreeMap<ObjId, Change> {
69 &self.overlay
70 }
71
72 pub fn is_modified(&self) -> bool {
74 !self.overlay.is_empty() || &self.trailer != self.file.trailer()
75 }
76
77 pub fn get(&self, id: ObjId) -> Result<Arc<Object>> {
79 match self.overlay.get(&id) {
80 Some(Change::Set(obj)) => Ok(Arc::clone(obj)),
81 Some(Change::Deleted) => Err(Error::MissingObject(id)),
82 None => self.file.get(id),
83 }
84 }
85
86 pub fn resolve(&self, object: &Object) -> Object {
89 let mut current = object.clone();
90 for _ in 0..self.file.limits().max_depth {
91 match current {
92 Object::Ref(id) => match self.get(id) {
93 Ok(o) => current = (*o).clone(),
94 Err(_) => return Object::Null,
95 },
96 other => return other,
97 }
98 }
99 Object::Null
100 }
101
102 pub fn resolve_dict(&self, object: &Object) -> Option<Dict> {
104 match self.resolve(object) {
105 Object::Dict(d) => Some(d),
106 Object::Stream(s) => Some(s.dict),
107 _ => None,
108 }
109 }
110
111 pub fn decode_stream(&self, stream: &Stream) -> Result<Decoded> {
114 let mut dict = stream.dict.clone();
115 for key in [&b"Filter"[..], b"DecodeParms"] {
116 if let Some(v) = dict.get(key).cloned() {
117 let resolved = match self.resolve(&v) {
118 Object::Array(items) => {
119 Object::Array(items.iter().map(|i| self.resolve(i)).collect())
120 }
121 other => other,
122 };
123 dict.insert(key, resolved);
124 }
125 }
126 otf_pdf_syntax::filters::decode(
127 &stream.raw,
128 &otf_pdf_syntax::filters::filter_chain(&dict),
129 self.file.limits(),
130 self.file.strictness(),
131 Some(self.file.diagnostics_sink()),
132 )
133 }
134
135 pub fn object_ids(&self) -> Vec<ObjId> {
137 let mut ids: BTreeMap<u32, ObjId> = BTreeMap::new();
138 for (&num, entry) in self.file.entries() {
139 match *entry {
140 XrefEntry::InUse { generation, .. } => {
141 ids.insert(num, ObjId::new(num, generation));
142 }
143 XrefEntry::Compressed { .. } => {
144 ids.insert(num, ObjId::new(num, 0));
145 }
146 XrefEntry::Free => {}
147 }
148 }
149 for (id, change) in &self.overlay {
150 match change {
151 Change::Set(_) => {
152 ids.insert(id.num, *id);
153 }
154 Change::Deleted => {
155 ids.remove(&id.num);
156 }
157 }
158 }
159 ids.into_values().collect()
160 }
161
162 pub fn set(&mut self, id: ObjId, object: Object) {
164 if id.num >= self.next_num {
165 self.next_num = id.num.saturating_add(1);
166 }
167 self.overlay.insert(id, Change::Set(Arc::new(object)));
168 }
169
170 pub fn add(&mut self, object: Object) -> Result<ObjId> {
172 let id = self.allocate()?;
173 self.set(id, object);
174 Ok(id)
175 }
176
177 pub fn allocate(&mut self) -> Result<ObjId> {
180 if self.next_num > self.file.limits().max_objects {
181 return Err(Error::Limit(LimitKind::Objects));
182 }
183 let id = ObjId::new(self.next_num, 0);
184 self.next_num += 1;
185 self.overlay.insert(id, Change::Set(Arc::new(Object::Null)));
187 Ok(id)
188 }
189
190 pub fn delete(&mut self, id: ObjId) {
192 self.overlay.insert(id, Change::Deleted);
193 }
194
195 pub fn next_number(&self) -> u32 {
197 self.next_num
198 }
199
200 pub fn limits(&self) -> &Limits {
202 self.file.limits()
203 }
204
205 pub fn strictness(&self) -> Strictness {
207 self.file.strictness()
208 }
209
210 pub fn diagnostics_sink(&self) -> &Diagnostics {
212 self.file.diagnostics_sink()
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219 use otf_pdf_syntax::OpenOptions;
220
221 fn store() -> Store {
222 let bytes = b"%PDF-1.7\n1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj\n\
223 2 0 obj << /Type /Pages /Kids [] /Count 0 >> endobj\n3 0 obj (base) endobj\n"
224 .to_vec();
225 Store::new(File::open(Arc::new(bytes), OpenOptions::default()).unwrap())
226 }
227
228 #[test]
229 fn overlay_shadows_base() {
230 let mut s = store();
231 assert!(!s.is_modified());
232 let three = ObjId::new(3, 0);
233 assert_eq!(s.get(three).unwrap().as_string(), Some(&b"base"[..]));
234 s.set(three, Object::Int(7));
235 assert_eq!(s.get(three).unwrap().as_int(), Some(7));
236 assert_eq!(s.file().get(three).unwrap().as_string(), Some(&b"base"[..]));
237 assert!(s.is_modified());
238 s.delete(three);
239 assert!(s.get(three).is_err());
240 assert_eq!(s.resolve(&Object::Ref(three)), Object::Null);
241 assert_eq!(s.object_ids(), vec![ObjId::new(1, 0), ObjId::new(2, 0)]);
242 }
243
244 #[test]
245 fn allocation_continues_after_the_base() {
246 let mut s = store();
247 let id = s.add(Object::Bool(true)).unwrap();
248 assert_eq!(id, ObjId::new(4, 0));
249 assert_eq!(s.next_number(), 5);
250 let reserved = s.allocate().unwrap();
251 assert_eq!(s.get(reserved).unwrap().as_ref(), &Object::Null);
252 s.set(ObjId::new(10, 0), Object::Null);
253 assert_eq!(s.next_number(), 11);
254 assert_eq!(s.object_ids().len(), 6);
255 }
256
257 #[test]
258 fn allocation_is_limited() {
259 let bytes = b"%PDF-1.7\n1 0 obj << /Type /Catalog >> endobj\n".to_vec();
260 let options = OpenOptions {
261 limits: Limits {
262 max_objects: 3,
263 ..Limits::default()
264 },
265 ..OpenOptions::default()
266 };
267 let mut s = Store::new(File::open(Arc::new(bytes), options).unwrap());
268 assert!(s.add(Object::Null).is_ok());
269 assert!(s.add(Object::Null).is_ok());
270 assert_eq!(s.add(Object::Null), Err(Error::Limit(LimitKind::Objects)));
271 }
272}