Skip to main content

pdf_objects/
types.rs

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}