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_integer(&self) -> Option<i64> {
55 match self {
56 PdfValue::Integer(value) => Some(*value),
57 PdfValue::Number(value) if value.fract() == 0.0 => Some(*value as i64),
58 _ => None,
59 }
60 }
61
62 pub fn as_number(&self) -> Option<f64> {
63 match self {
64 PdfValue::Integer(value) => Some(*value as f64),
65 PdfValue::Number(value) => Some(*value),
66 _ => None,
67 }
68 }
69
70 pub fn as_array(&self) -> Option<&[PdfValue]> {
71 match self {
72 PdfValue::Array(values) => Some(values),
73 _ => None,
74 }
75 }
76
77 pub fn as_dictionary(&self) -> Option<&PdfDictionary> {
78 match self {
79 PdfValue::Dictionary(dictionary) => Some(dictionary),
80 _ => None,
81 }
82 }
83}
84
85#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
86pub struct PdfStream {
87 pub dict: PdfDictionary,
88 pub data: Vec<u8>,
89}
90
91#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
92pub enum PdfObject {
93 Value(PdfValue),
94 Stream(PdfStream),
95}
96
97#[derive(Debug, Clone, PartialEq)]
98pub enum XrefEntry {
99 Free,
100 Uncompressed {
101 offset: usize,
102 generation: u16,
103 },
104 Compressed {
105 stream_object_number: u32,
106 index: u32,
107 },
108}
109
110#[derive(Debug, Clone)]
111pub struct PdfFile {
112 pub version: String,
113 pub objects: BTreeMap<ObjectRef, PdfObject>,
114 pub trailer: PdfDictionary,
115 pub max_object_number: u32,
116}
117
118impl PdfFile {
119 pub fn get_object(&self, object_ref: ObjectRef) -> PdfResult<&PdfObject> {
120 self.objects.get(&object_ref).ok_or_else(|| {
121 PdfError::MissingObject(format!(
122 "{} {}",
123 object_ref.object_number, object_ref.generation
124 ))
125 })
126 }
127
128 pub fn get_object_mut(&mut self, object_ref: ObjectRef) -> PdfResult<&mut PdfObject> {
129 self.objects.get_mut(&object_ref).ok_or_else(|| {
130 PdfError::MissingObject(format!(
131 "{} {}",
132 object_ref.object_number, object_ref.generation
133 ))
134 })
135 }
136
137 pub fn get_value(&self, object_ref: ObjectRef) -> PdfResult<&PdfValue> {
138 match self.get_object(object_ref)? {
139 PdfObject::Value(value) => Ok(value),
140 PdfObject::Stream(_) => Err(PdfError::Corrupt(format!(
141 "expected value object at {} {}",
142 object_ref.object_number, object_ref.generation
143 ))),
144 }
145 }
146
147 pub fn get_dictionary(&self, object_ref: ObjectRef) -> PdfResult<&PdfDictionary> {
148 match self.get_value(object_ref)? {
149 PdfValue::Dictionary(dictionary) => Ok(dictionary),
150 _ => Err(PdfError::Corrupt(format!(
151 "expected dictionary at {} {}",
152 object_ref.object_number, object_ref.generation
153 ))),
154 }
155 }
156
157 pub fn resolve<'a>(&'a self, value: &'a PdfValue) -> PdfResult<&'a PdfValue> {
158 match value {
159 PdfValue::Reference(object_ref) => self.get_value(*object_ref),
160 _ => Ok(value),
161 }
162 }
163
164 pub fn resolve_dict<'a>(&'a self, value: &'a PdfValue) -> PdfResult<&'a PdfDictionary> {
165 self.resolve(value)?
166 .as_dictionary()
167 .ok_or_else(|| PdfError::Corrupt("expected dictionary value".to_string()))
168 }
169
170 pub fn allocate_object_ref(&mut self) -> ObjectRef {
171 self.max_object_number += 1;
172 ObjectRef::new(self.max_object_number, 0)
173 }
174
175 pub fn insert_object(&mut self, object_ref: ObjectRef, object: PdfObject) {
176 self.max_object_number = self.max_object_number.max(object_ref.object_number);
177 self.objects.insert(object_ref, object);
178 }
179}