pdf_lib_rs/core/
context.rs1use std::collections::HashMap;
2use crate::core::document::PdfHeader;
3use crate::core::objects::{PdfObject, PdfRef};
4
5#[derive(Debug)]
8pub struct PdfContext {
9 pub largest_object_number: u32,
10 pub header: PdfHeader,
11 pub trailer_info: TrailerInfo,
12 indirect_objects: HashMap<PdfRefKey, PdfObject>,
13}
14
15#[derive(Debug, Clone, Hash, PartialEq, Eq)]
17struct PdfRefKey {
18 object_number: u32,
19 generation_number: u16,
20}
21
22impl From<&PdfRef> for PdfRefKey {
23 fn from(r: &PdfRef) -> Self {
24 PdfRefKey {
25 object_number: r.object_number,
26 generation_number: r.generation_number,
27 }
28 }
29}
30
31#[derive(Debug, Default)]
33pub struct TrailerInfo {
34 pub root: Option<PdfObject>,
35 pub encrypt: Option<PdfObject>,
36 pub info: Option<PdfObject>,
37 pub id: Option<PdfObject>,
38}
39
40impl PdfContext {
41 pub fn create() -> Self {
42 PdfContext {
43 largest_object_number: 0,
44 header: PdfHeader::for_version(1, 7),
45 trailer_info: TrailerInfo::default(),
46 indirect_objects: HashMap::new(),
47 }
48 }
49
50 pub fn assign(&mut self, pdf_ref: &PdfRef, object: PdfObject) {
52 if pdf_ref.object_number > self.largest_object_number {
53 self.largest_object_number = pdf_ref.object_number;
54 }
55 self.indirect_objects.insert(PdfRefKey::from(pdf_ref), object);
56 }
57
58 pub fn next_ref(&mut self) -> PdfRef {
60 self.largest_object_number += 1;
61 PdfRef::of(self.largest_object_number, 0)
62 }
63
64 pub fn register(&mut self, object: PdfObject) -> PdfRef {
66 let pdf_ref = self.next_ref();
67 self.assign(&pdf_ref, object);
68 pdf_ref
69 }
70
71 pub fn lookup(&self, pdf_ref: &PdfRef) -> Option<&PdfObject> {
73 self.indirect_objects.get(&PdfRefKey::from(pdf_ref))
74 }
75
76 pub fn delete(&mut self, pdf_ref: &PdfRef) -> bool {
78 self.indirect_objects.remove(&PdfRefKey::from(pdf_ref)).is_some()
79 }
80
81 pub fn enumerate_indirect_objects(&self) -> Vec<(PdfRef, &PdfObject)> {
83 let mut entries: Vec<_> = self
84 .indirect_objects
85 .iter()
86 .map(|(k, v)| (PdfRef::of(k.object_number, k.generation_number), v))
87 .collect();
88 entries.sort_by_key(|(r, _)| r.object_number);
89 entries
90 }
91
92 pub fn object_count(&self) -> usize {
94 self.indirect_objects.len()
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use crate::core::objects::{PdfNumber, PdfName};
102
103 #[test]
104 fn can_create_context() {
105 let ctx = PdfContext::create();
106 assert_eq!(ctx.largest_object_number, 0);
107 assert_eq!(ctx.object_count(), 0);
108 }
109
110 #[test]
111 fn can_assign_and_lookup() {
112 let mut ctx = PdfContext::create();
113 let r = PdfRef::of(1, 0);
114 ctx.assign(&r, PdfObject::Number(PdfNumber::of(42.0)));
115 assert!(ctx.lookup(&r).is_some());
116 assert_eq!(ctx.largest_object_number, 1);
117 }
118
119 #[test]
120 fn can_register_objects() {
121 let mut ctx = PdfContext::create();
122 let r1 = ctx.register(PdfObject::Name(PdfName::of("Foo")));
123 let r2 = ctx.register(PdfObject::Name(PdfName::of("Bar")));
124 assert_eq!(r1.object_number, 1);
125 assert_eq!(r2.object_number, 2);
126 assert_eq!(ctx.object_count(), 2);
127 }
128
129 #[test]
130 fn can_delete_objects() {
131 let mut ctx = PdfContext::create();
132 let r = ctx.register(PdfObject::Number(PdfNumber::of(1.0)));
133 assert!(ctx.delete(&r));
134 assert!(ctx.lookup(&r).is_none());
135 }
136
137 #[test]
138 fn can_enumerate_objects() {
139 let mut ctx = PdfContext::create();
140 ctx.register(PdfObject::Number(PdfNumber::of(1.0)));
141 ctx.register(PdfObject::Number(PdfNumber::of(2.0)));
142 let objects = ctx.enumerate_indirect_objects();
143 assert_eq!(objects.len(), 2);
144 assert_eq!(objects[0].0.object_number, 1);
145 assert_eq!(objects[1].0.object_number, 2);
146 }
147}