Skip to main content

pdf_writer/
buf.rs

1use std::ops::Deref;
2
3use super::Primitive;
4
5/// A buffer of arbitrary PDF content.
6#[derive(Debug, Clone, PartialEq)]
7pub struct Buf {
8    pub(crate) inner: Vec<u8>,
9    pub(crate) limits: Limits,
10}
11
12impl Buf {
13    pub(crate) fn new() -> Self {
14        Self { inner: Vec::new(), limits: Limits::new() }
15    }
16
17    pub(crate) fn with_capacity(capacity: usize) -> Self {
18        Self {
19            inner: Vec::with_capacity(capacity),
20            limits: Limits::new(),
21        }
22    }
23
24    /// Get the underlying bytes of the buffer.
25    pub fn into_vec(self) -> Vec<u8> {
26        self.inner
27    }
28
29    /// Get the underlying bytes of the buffer as a slice.
30    pub fn as_slice(&self) -> &[u8] {
31        self.inner.as_slice()
32    }
33
34    /// Return the limits of the buffer.
35    pub fn limits(&self) -> &Limits {
36        &self.limits
37    }
38
39    #[inline]
40    pub(crate) fn push_val<T: Primitive>(&mut self, value: T) {
41        value.write(self);
42    }
43
44    #[inline]
45    pub(crate) fn push_int(&mut self, value: i32) {
46        self.limits.register_int(value);
47        self.extend(itoa::Buffer::new().format(value).as_bytes());
48    }
49
50    #[inline]
51    pub(crate) fn push_float(&mut self, value: f32) {
52        // Don't write the decimal point if we don't need it.
53        // Also, integer formatting is way faster.
54        if value as i32 as f32 == value {
55            self.push_int(value as i32);
56        } else {
57            self.push_decimal(value);
58        }
59    }
60
61    /// Like `push_float`, but forces the decimal point.
62    #[inline]
63    pub(crate) fn push_decimal(&mut self, value: f32) {
64        self.limits.register_real(value);
65
66        if value == 0.0 || (value.abs() > 1e-6 && value.abs() < 1e12) {
67            self.extend(ryu::Buffer::new().format(value).as_bytes());
68        } else {
69            #[inline(never)]
70            fn write_extreme(buf: &mut Buf, value: f32) {
71                use std::io::Write;
72                write!(buf.inner, "{value}").unwrap();
73            }
74
75            write_extreme(self, value);
76        }
77    }
78
79    #[inline]
80    pub(crate) fn extend_buf(&mut self, other: &Buf) {
81        self.limits.merge(&other.limits);
82        self.inner.extend(&other.inner);
83    }
84
85    #[inline]
86    pub(crate) fn push(&mut self, b: u8) {
87        self.inner.push(b);
88    }
89
90    #[inline]
91    pub(crate) fn push_hex(&mut self, value: u8) {
92        fn hex(b: u8) -> u8 {
93            if b < 10 {
94                b'0' + b
95            } else {
96                b'A' + (b - 10)
97            }
98        }
99
100        self.push(hex(value >> 4));
101        self.push(hex(value & 0xF));
102    }
103
104    #[inline]
105    pub(crate) fn push_hex_u16(&mut self, value: u16) {
106        self.push_hex((value >> 8) as u8);
107        self.push_hex(value as u8);
108    }
109
110    #[inline]
111    pub(crate) fn push_octal(&mut self, value: u8) {
112        fn octal(b: u8) -> u8 {
113            b'0' + b
114        }
115
116        self.push(octal(value >> 6));
117        self.push(octal((value >> 3) & 7));
118        self.push(octal(value & 7));
119    }
120
121    #[inline]
122    pub(crate) fn reserve(&mut self, additional: usize) {
123        self.inner.reserve(additional)
124    }
125}
126
127impl Deref for Buf {
128    type Target = [u8];
129
130    fn deref(&self) -> &Self::Target {
131        &self.inner
132    }
133}
134
135impl Extend<u8> for Buf {
136    fn extend<T: IntoIterator<Item = u8>>(&mut self, iter: T) {
137        self.inner.extend(iter)
138    }
139}
140
141impl<'a> Extend<&'a u8> for Buf {
142    fn extend<T: IntoIterator<Item = &'a u8>>(&mut self, iter: T) {
143        self.inner.extend(iter)
144    }
145}
146
147/// Tracks the limits of data types used in a buffer.
148#[derive(Debug, Default, Clone, PartialEq)]
149pub struct Limits {
150    int: i32,
151    real: f32,
152    name_len: usize,
153    str_len: usize,
154    array_len: usize,
155    dict_entries: usize,
156}
157
158impl Limits {
159    /// Create a new `Limits` struct with all values initialized to zero.
160    pub fn new() -> Self {
161        Self::default()
162    }
163
164    /// Get the absolute value of the largest positive/negative integer number.
165    pub fn int(&self) -> i32 {
166        self.int
167    }
168
169    /// Get the absolute value of the largest positive/negative real number.
170    pub fn real(&self) -> f32 {
171        self.real
172    }
173
174    /// Get the maximum length of any used name.
175    pub fn name_len(&self) -> usize {
176        self.name_len
177    }
178
179    /// Get the maximum length of any used string.
180    pub fn str_len(&self) -> usize {
181        self.str_len
182    }
183
184    /// Get the maximum length of any used array.
185    pub fn array_len(&self) -> usize {
186        self.array_len
187    }
188
189    /// Get the maximum number of entries in any dictionary.
190    pub fn dict_entries(&self) -> usize {
191        self.dict_entries
192    }
193
194    pub(crate) fn register_int(&mut self, val: i32) {
195        self.int = self.int.max(val.abs());
196    }
197
198    pub(crate) fn register_real(&mut self, val: f32) {
199        self.real = self.real.max(val.abs());
200    }
201
202    pub(crate) fn register_name_len(&mut self, len: usize) {
203        self.name_len = self.name_len.max(len);
204    }
205
206    pub(crate) fn register_str_len(&mut self, len: usize) {
207        self.str_len = self.str_len.max(len);
208    }
209
210    pub(crate) fn register_array_len(&mut self, len: usize) {
211        self.array_len = self.array_len.max(len);
212    }
213
214    pub(crate) fn register_dict_entries(&mut self, len: usize) {
215        self.dict_entries = self.dict_entries.max(len);
216    }
217
218    /// Merge two `Limits` with each other, taking the maximum
219    /// of each field from both.
220    pub fn merge(&mut self, other: &Limits) {
221        self.register_int(other.int);
222        self.register_real(other.real);
223        self.register_name_len(other.name_len);
224        self.register_str_len(other.str_len);
225        self.register_array_len(other.array_len);
226        self.register_dict_entries(other.dict_entries);
227    }
228}
229
230#[cfg(test)]
231mod tests {
232    use super::*;
233    use crate::{Chunk, Content, Finish, Name, Rect, Ref, Str, TextStr};
234
235    #[test]
236    fn test_content_limits() {
237        let mut limits = Limits::default();
238
239        let mut content = Content::new();
240        content.cubic_to(14.3, 16.2, 22.6, 30.9, 50.1, 40.0);
241        content.show(Str(b"Some text"));
242        content.set_font(Name(b"NotoSans"), 10.0);
243        let buf = content.finish();
244        limits.merge(buf.limits());
245
246        let mut content = Content::new();
247        content.line_to(55.0, -75.3);
248        content.set_font(Name(b"Noto"), 10.0);
249        content
250            .show_positioned()
251            .items()
252            .show(Str(b"A"))
253            .show(Str(b"B"))
254            .adjust(32.0);
255        content
256            .marked_content_point_with_properties(Name(b"Hi"))
257            .properties()
258            .actual_text(TextStr("text"));
259        let buf = content.finish();
260        limits.merge(buf.limits());
261
262        assert_eq!(
263            limits,
264            Limits {
265                int: 55,
266                real: 75.3,
267                name_len: 10,
268                str_len: 9,
269                array_len: 3,
270                dict_entries: 1,
271            }
272        )
273    }
274
275    #[test]
276    fn test_chunk_limits() {
277        let mut limits = Limits::default();
278
279        let mut chunk = Chunk::new();
280        let mut x_object = chunk.form_xobject(Ref::new(1), &[]);
281        x_object.bbox(Rect::new(4.0, 6.0, 22.1, 31.0));
282        x_object.finish();
283
284        limits.merge(chunk.limits());
285
286        assert_eq!(
287            limits,
288            Limits {
289                int: 31,
290                real: 22.1,
291                name_len: 7,
292                str_len: 0,
293                array_len: 4,
294                dict_entries: 4,
295            }
296        )
297    }
298}