1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5use crate::error::{PdfError, PdfResult};
6
7pub type PdfDictionary = BTreeMap<String, PdfValue>;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
10pub struct ObjectRef {
11 pub object_number: u32,
12 pub generation: u16,
13}
14
15impl ObjectRef {
16 pub const fn new(object_number: u32, generation: u16) -> Self {
17 Self {
18 object_number,
19 generation,
20 }
21 }
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
25pub struct PdfString(pub Vec<u8>);
26
27impl PdfString {
28 pub fn to_lossy_string(&self) -> String {
29 String::from_utf8_lossy(&self.0).into_owned()
30 }
31}
32
33#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
34pub enum PdfValue {
35 Null,
36 Bool(bool),
37 Integer(i64),
38 Number(f64),
39 Name(String),
40 String(PdfString),
41 Array(Vec<PdfValue>),
42 Dictionary(PdfDictionary),
43 Reference(ObjectRef),
44}
45
46impl PdfValue {
47 pub fn as_name(&self) -> Option<&str> {
48 match self {
49 PdfValue::Name(value) => Some(value.as_str()),
50 _ => None,
51 }
52 }
53
54 pub fn as_bool(&self) -> Option<bool> {
55 match self {
56 PdfValue::Bool(value) => Some(*value),
57 _ => None,
58 }
59 }
60
61 pub fn as_integer(&self) -> Option<i64> {
62 match self {
63 PdfValue::Integer(value) => Some(*value),
64 PdfValue::Number(value) if value.fract() == 0.0 => Some(*value as i64),
65 _ => None,
66 }
67 }
68
69 pub fn as_number(&self) -> Option<f64> {
70 match self {
71 PdfValue::Integer(value) => Some(*value as f64),
72 PdfValue::Number(value) => Some(*value),
73 _ => None,
74 }
75 }
76
77 pub fn as_array(&self) -> Option<&[PdfValue]> {
78 match self {
79 PdfValue::Array(values) => Some(values),
80 _ => None,
81 }
82 }
83
84 pub fn as_dictionary(&self) -> Option<&PdfDictionary> {
85 match self {
86 PdfValue::Dictionary(dictionary) => Some(dictionary),
87 _ => None,
88 }
89 }
90}
91
92#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
93pub struct PdfStream {
94 pub dict: PdfDictionary,
95 pub data: Vec<u8>,
96}
97
98#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
99pub enum PdfObject {
100 Value(PdfValue),
101 Stream(PdfStream),
102}
103
104#[derive(Debug, Clone, PartialEq)]
105pub enum XrefEntry {
106 Free,
107 Uncompressed {
108 offset: usize,
109 generation: u16,
110 },
111 Compressed {
112 stream_object_number: u32,
113 index: u32,
114 },
115}
116
117#[derive(Debug, Clone)]
118pub struct PdfFile {
119 pub version: String,
120 pub objects: BTreeMap<ObjectRef, PdfObject>,
121 pub trailer: PdfDictionary,
122 pub max_object_number: u32,
123}
124
125impl PdfFile {
126 pub fn get_object(&self, object_ref: ObjectRef) -> PdfResult<&PdfObject> {
127 self.objects.get(&object_ref).ok_or_else(|| {
128 PdfError::MissingObject(format!(
129 "{} {}",
130 object_ref.object_number, object_ref.generation
131 ))
132 })
133 }
134
135 pub fn get_object_mut(&mut self, object_ref: ObjectRef) -> PdfResult<&mut PdfObject> {
136 self.objects.get_mut(&object_ref).ok_or_else(|| {
137 PdfError::MissingObject(format!(
138 "{} {}",
139 object_ref.object_number, object_ref.generation
140 ))
141 })
142 }
143
144 pub fn get_value(&self, object_ref: ObjectRef) -> PdfResult<&PdfValue> {
145 match self.get_object(object_ref)? {
146 PdfObject::Value(value) => Ok(value),
147 PdfObject::Stream(_) => Err(PdfError::Corrupt(format!(
148 "expected value object at {} {}",
149 object_ref.object_number, object_ref.generation
150 ))),
151 }
152 }
153
154 pub fn get_dictionary(&self, object_ref: ObjectRef) -> PdfResult<&PdfDictionary> {
155 match self.get_value(object_ref)? {
156 PdfValue::Dictionary(dictionary) => Ok(dictionary),
157 _ => Err(PdfError::Corrupt(format!(
158 "expected dictionary at {} {}",
159 object_ref.object_number, object_ref.generation
160 ))),
161 }
162 }
163
164 pub fn resolve<'a>(&'a self, value: &'a PdfValue) -> PdfResult<&'a PdfValue> {
165 match value {
166 PdfValue::Reference(object_ref) => self.get_value(*object_ref),
167 _ => Ok(value),
168 }
169 }
170
171 pub fn resolve_dict<'a>(&'a self, value: &'a PdfValue) -> PdfResult<&'a PdfDictionary> {
172 self.resolve(value)?
173 .as_dictionary()
174 .ok_or_else(|| PdfError::Corrupt("expected dictionary value".to_string()))
175 }
176
177 pub fn allocate_object_ref(&mut self) -> ObjectRef {
178 self.max_object_number += 1;
179 ObjectRef::new(self.max_object_number, 0)
180 }
181
182 pub fn insert_object(&mut self, object_ref: ObjectRef, object: PdfObject) {
183 self.max_object_number = self.max_object_number.max(object_ref.object_number);
184 self.objects.insert(object_ref, object);
185 }
186}