graphitepdf_kit/
objects.rs1use crate::error::{GraphitePdfKitError, Result};
2use std::io::Write;
3
4#[cfg(feature = "tracing")]
5use tracing::instrument;
6
7#[derive(Clone, Debug, PartialEq)]
9pub enum Object {
10 Null,
12 Boolean(bool),
14 Integer(i64),
16 Real(f64),
18 String(Vec<u8>),
20 Name(String),
22 Array(Vec<Object>),
24 Dict(Vec<(String, Object)>),
26 Ref(u64),
28 Stream {
30 dict: Vec<(String, Object)>,
31 data: Vec<u8>,
32 },
33}
34
35impl Object {
36 pub fn name(s: impl Into<String>) -> Self {
38 Object::Name(s.into())
39 }
40
41 pub fn dict(entries: impl IntoIterator<Item = (impl Into<String>, impl Into<Object>)>) -> Self {
43 Object::Dict(
44 entries
45 .into_iter()
46 .map(|(k, v)| (k.into(), v.into()))
47 .collect(),
48 )
49 }
50
51 pub fn array(items: impl IntoIterator<Item = impl Into<Object>>) -> Self {
53 Object::Array(items.into_iter().map(|i| i.into()).collect())
54 }
55
56 pub fn string(s: impl AsRef<[u8]>) -> Self {
58 Object::String(s.as_ref().to_vec())
59 }
60
61 pub fn string_utf16(s: &str) -> Self {
63 let mut bytes = vec![0xFE, 0xFF]; for c in s.chars() {
65 let code = c as u16;
66 bytes.push((code >> 8) as u8);
67 bytes.push((code & 0xFF) as u8);
68 }
69 Object::String(bytes)
70 }
71
72 pub fn integer(n: i64) -> Self {
74 Object::Integer(n)
75 }
76
77 pub fn real(n: f64) -> Self {
79 Object::Real(n)
80 }
81
82 pub fn insert(&mut self, key: impl Into<String>, value: impl Into<Object>) -> Result<()> {
85 match self {
86 Object::Dict(entries) => {
87 let key_str = key.into();
88 entries.retain(|(k, _)| k != &key_str);
89 entries.push((key_str, value.into()));
90 Ok(())
91 }
92 _ => Err(GraphitePdfKitError::InvalidObject(
93 "Cannot insert into non-Dictionary object".to_string(),
94 )),
95 }
96 }
97
98 pub fn get(&self, key: &str) -> Option<&Object> {
101 match self {
102 Object::Dict(entries) => entries.iter().find(|(k, _)| k == key).map(|(_, v)| v),
103 _ => None,
104 }
105 }
106
107 pub fn push(&mut self, item: impl Into<Object>) -> Result<()> {
110 match self {
111 Object::Array(items) => {
112 items.push(item.into());
113 Ok(())
114 }
115 _ => Err(GraphitePdfKitError::InvalidObject(
116 "Cannot push into non-Array object".to_string(),
117 )),
118 }
119 }
120
121 #[cfg_attr(feature = "tracing", instrument(skip(writer)))]
123 pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
124 match self {
125 Object::Null => writer.write_all(b"null")?,
126 Object::Boolean(true) => writer.write_all(b"true")?,
127 Object::Boolean(false) => writer.write_all(b"false")?,
128 Object::Integer(n) => write!(writer, "{n}")?,
129 Object::Real(n) => {
130 if n.is_infinite() || n.is_nan() {
131 return Err(GraphitePdfKitError::InvalidObject(
132 "cannot serialize infinite or NaN real number".to_string(),
133 ));
134 }
135 write!(writer, "{n}")?;
136 }
137 Object::String(s) => {
138 writer.write_all(b"(")?;
139 for byte in s {
140 match byte {
141 b'(' | b')' | b'\\' => {
142 writer.write_all(b"\\")?;
143 writer.write_all(&[*byte])?;
144 }
145 0x08 => writer.write_all(b"\\b")?,
146 0x09 => writer.write_all(b"\\t")?,
147 0x0a => writer.write_all(b"\\n")?,
148 0x0c => writer.write_all(b"\\f")?,
149 0x0d => writer.write_all(b"\\r")?,
150 _ => writer.write_all(&[*byte])?,
151 }
152 }
153 writer.write_all(b")")?;
154 }
155 Object::Name(name) => {
156 writer.write_all(b"/")?;
157 for byte in name.bytes() {
158 match byte {
159 b'0'..=b'9'
160 | b'A'..=b'Z'
161 | b'a'..=b'z'
162 | b'_'
163 | b';'
164 | b':'
165 | b'@'
166 | b'&'
167 | b'$'
168 | b'#'
169 | b'%'
170 | b'+'
171 | b'-'
172 | b'.'
173 | b'!'
174 | b'~'
175 | b'*'
176 | b'/'
177 | b'?' => {
178 writer.write_all(&[byte])?;
179 }
180 _ => {
181 write!(writer, "#{:02x}", byte)?;
182 }
183 }
184 }
185 }
186 Object::Array(items) => {
187 writer.write_all(b"[")?;
188 for (i, item) in items.iter().enumerate() {
189 if i > 0 {
190 writer.write_all(b" ")?;
191 }
192 item.write(writer)?;
193 }
194 writer.write_all(b"]")?;
195 }
196 Object::Dict(entries) => {
197 writer.write_all(b"<<")?;
198 for (i, (key, value)) in entries.iter().enumerate() {
199 if i > 0 {
200 writer.write_all(b"\n")?;
201 }
202 writer.write_all(b"/")?;
203 writer.write_all(key.as_bytes())?;
204 writer.write_all(b" ")?;
205 value.write(writer)?;
206 }
207 writer.write_all(b">>")?;
208 }
209 Object::Ref(n) => {
210 write!(writer, "{n} 0 R")?;
211 }
212 Object::Stream { dict, data } => {
213 let mut dict_with_length = dict.clone();
214 dict_with_length.push(("Length".to_string(), Object::Integer(data.len() as i64)));
215 Object::Dict(dict_with_length).write(writer)?;
216 writer.write_all(b"\nstream\n")?;
217 writer.write_all(data)?;
218 writer.write_all(b"\nendstream")?;
219 }
220 }
221 Ok(())
222 }
223}
224
225impl From<bool> for Object {
226 fn from(value: bool) -> Self {
227 Object::Boolean(value)
228 }
229}
230
231impl From<i64> for Object {
232 fn from(value: i64) -> Self {
233 Object::Integer(value)
234 }
235}
236
237impl From<i32> for Object {
238 fn from(value: i32) -> Self {
239 Object::Integer(value as i64)
240 }
241}
242
243impl From<f64> for Object {
244 fn from(value: f64) -> Self {
245 Object::Real(value)
246 }
247}
248
249impl From<f32> for Object {
250 fn from(value: f32) -> Self {
251 Object::Real(value as f64)
252 }
253}
254
255impl From<&str> for Object {
256 fn from(value: &str) -> Self {
257 Object::string(value.as_bytes())
258 }
259}
260
261impl From<String> for Object {
262 fn from(value: String) -> Self {
263 Object::string(value.into_bytes())
264 }
265}
266
267impl<T: Into<Object>> From<Vec<T>> for Object {
268 fn from(value: Vec<T>) -> Self {
269 Object::Array(value.into_iter().map(|v| v.into()).collect())
270 }
271}
272
273impl<'a, T: Into<Object> + Clone> From<&'a [T]> for Object {
274 fn from(value: &'a [T]) -> Self {
275 Object::Array(value.iter().map(|v| v.clone().into()).collect())
276 }
277}