Skip to content

Commit 4a5db97

Browse files
authored
Support for tracking PDF limits (#47)
1 parent a83ece3 commit 4a5db97

File tree

10 files changed

+395
-89
lines changed

10 files changed

+395
-89
lines changed

benches/oneshot.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ fn bench_content() -> Vec<u8> {
4747
c.save_state();
4848
c.set_flatness(10);
4949
c.restore_state();
50-
c.finish()
50+
c.finish().into_vec()
5151
}
5252

5353
fn bench_new() -> Pdf {

examples/limits.rs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//! This example shows how you can track PDF limits of your chunks.
2+
3+
use pdf_writer::{Chunk, Content, Limits, Name, Ref};
4+
5+
fn main() {
6+
let mut limits = Limits::new();
7+
8+
let mut content = Content::new();
9+
content.transform([-3.4, 0.0, 0.0, 3.1, 100.0, 100.0]);
10+
content.line_to(15.0, -26.1);
11+
let buf = content.finish();
12+
13+
// This will have the limits:
14+
// - Max real number: 26.1 (for negative values we use their absolute value)
15+
// - Max int number 100 (even though above 100.0 is a float number, it will be coerced into an
16+
// integer, and thus counts towards the int limit)
17+
limits.merge(buf.limits());
18+
19+
let mut chunk = Chunk::new();
20+
chunk.stream(Ref::new(1), &buf.into_vec());
21+
chunk.type3_font(Ref::new(2)).name(Name(b"A_long_font_name"));
22+
23+
// This will update the limit for the maximum name and dictionary length.
24+
limits.merge(chunk.limits());
25+
26+
// The combined limits of the content and the chunk.
27+
assert_eq!(limits.int(), 100);
28+
assert_eq!(limits.real(), 26.1);
29+
assert_eq!(limits.name_len(), 16);
30+
assert_eq!(limits.dict_entries(), 3);
31+
}

src/buf.rs

+235-19
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,54 @@
1+
use std::ops::Deref;
2+
13
use super::Primitive;
24

3-
/// Additional methods for byte buffers.
4-
pub trait BufExt {
5-
fn push_val<T: Primitive>(&mut self, value: T);
6-
fn push_int(&mut self, value: i32);
7-
fn push_float(&mut self, value: f32);
8-
fn push_decimal(&mut self, value: f32);
9-
fn push_hex(&mut self, value: u8);
10-
fn push_hex_u16(&mut self, value: u16);
11-
fn push_octal(&mut self, value: u8);
5+
/// A buffer of arbitrary PDF content.
6+
#[derive(Debug, Clone, PartialEq)]
7+
pub struct Buf {
8+
pub(crate) inner: Vec<u8>,
9+
pub(crate) limits: Limits,
1210
}
1311

14-
impl BufExt for Vec<u8> {
12+
impl 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+
1539
#[inline]
16-
fn push_val<T: Primitive>(&mut self, value: T) {
40+
pub(crate) fn push_val<T: Primitive>(&mut self, value: T) {
1741
value.write(self);
1842
}
1943

2044
#[inline]
21-
fn push_int(&mut self, value: i32) {
45+
pub(crate) fn push_int(&mut self, value: i32) {
46+
self.limits.register_int(value);
2247
self.extend(itoa::Buffer::new().format(value).as_bytes());
2348
}
2449

2550
#[inline]
26-
fn push_float(&mut self, value: f32) {
51+
pub(crate) fn push_float(&mut self, value: f32) {
2752
// Don't write the decimal point if we don't need it.
2853
// Also, integer formatting is way faster.
2954
if value as i32 as f32 == value {
@@ -35,22 +60,35 @@ impl BufExt for Vec<u8> {
3560

3661
/// Like `push_float`, but forces the decimal point.
3762
#[inline]
38-
fn push_decimal(&mut self, value: f32) {
63+
pub(crate) fn push_decimal(&mut self, value: f32) {
64+
self.limits.register_real(value);
65+
3966
if value == 0.0 || (value.abs() > 1e-6 && value.abs() < 1e12) {
4067
self.extend(ryu::Buffer::new().format(value).as_bytes());
4168
} else {
4269
#[inline(never)]
43-
fn write_extreme(buf: &mut Vec<u8>, value: f32) {
70+
fn write_extreme(buf: &mut Buf, value: f32) {
4471
use std::io::Write;
45-
write!(buf, "{}", value).unwrap();
72+
write!(buf.inner, "{}", value).unwrap();
4673
}
4774

4875
write_extreme(self, value);
4976
}
5077
}
5178

5279
#[inline]
53-
fn push_hex(&mut self, value: u8) {
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) {
5492
fn hex(b: u8) -> u8 {
5593
if b < 10 {
5694
b'0' + b
@@ -64,13 +102,13 @@ impl BufExt for Vec<u8> {
64102
}
65103

66104
#[inline]
67-
fn push_hex_u16(&mut self, value: u16) {
105+
pub(crate) fn push_hex_u16(&mut self, value: u16) {
68106
self.push_hex((value >> 8) as u8);
69107
self.push_hex(value as u8);
70108
}
71109

72110
#[inline]
73-
fn push_octal(&mut self, value: u8) {
111+
pub(crate) fn push_octal(&mut self, value: u8) {
74112
fn octal(b: u8) -> u8 {
75113
b'0' + b
76114
}
@@ -79,4 +117,182 @@ impl BufExt for Vec<u8> {
79117
self.push(octal((value >> 3) & 7));
80118
self.push(octal(value & 7));
81119
}
120+
121+
#[inline]
122+
pub(crate) fn reserve(&mut self, additional: usize) {
123+
self.inner.reserve(additional)
124+
}
125+
}
126+
127+
impl Deref for Buf {
128+
type Target = [u8];
129+
130+
fn deref(&self) -> &Self::Target {
131+
&self.inner
132+
}
133+
}
134+
135+
impl Extend<u8> for Buf {
136+
fn extend<T: IntoIterator<Item = u8>>(&mut self, iter: T) {
137+
self.inner.extend(iter)
138+
}
139+
}
140+
141+
impl<'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)]
149+
pub 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+
158+
impl 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)]
231+
mod 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+
}
82298
}

0 commit comments

Comments
 (0)