1#[derive(Debug, Clone)]
43pub struct ProcessingLimits {
44 pub max_file_bytes: u64,
46 pub max_stream_bytes: u64,
49 pub max_total_memory_bytes: u64,
51 pub max_object_depth: u32,
53 pub max_operator_count: u64,
55 pub max_image_pixels: u64,
57 pub max_xfa_nesting_depth: u32,
59 pub max_formcalc_depth: u32,
61}
62
63impl Default for ProcessingLimits {
64 fn default() -> Self {
65 Self {
66 max_file_bytes: 500 * 1024 * 1024, max_stream_bytes: 256 * 1024 * 1024, max_total_memory_bytes: 1024 * 1024 * 1024, max_object_depth: 100,
70 max_operator_count: 10_000_000,
71 max_image_pixels: 16384 * 16384, max_xfa_nesting_depth: 50,
73 max_formcalc_depth: 200,
74 }
75 }
76}
77
78impl ProcessingLimits {
79 pub fn new() -> Self {
81 Self::default()
82 }
83
84 pub fn wasm() -> Self {
91 Self {
92 max_file_bytes: 50 * 1024 * 1024, max_stream_bytes: 32 * 1024 * 1024, max_total_memory_bytes: 128 * 1024 * 1024, max_object_depth: 50,
96 max_operator_count: 1_000_000,
97 max_image_pixels: 8192 * 8192, max_xfa_nesting_depth: 30,
99 max_formcalc_depth: 100,
100 }
101 }
102
103 pub fn unlimited() -> Self {
105 Self {
106 max_file_bytes: u64::MAX,
107 max_stream_bytes: u64::MAX,
108 max_total_memory_bytes: u64::MAX,
109 max_object_depth: u32::MAX,
110 max_operator_count: u64::MAX,
111 max_image_pixels: u64::MAX,
112 max_xfa_nesting_depth: u32::MAX,
113 max_formcalc_depth: u32::MAX,
114 }
115 }
116
117 pub fn max_file_bytes(mut self, bytes: u64) -> Self {
119 self.max_file_bytes = bytes;
120 self
121 }
122
123 pub fn max_stream_bytes(mut self, bytes: u64) -> Self {
125 self.max_stream_bytes = bytes;
126 self
127 }
128
129 pub fn max_total_memory_bytes(mut self, bytes: u64) -> Self {
131 self.max_total_memory_bytes = bytes;
132 self
133 }
134
135 pub fn max_object_depth(mut self, depth: u32) -> Self {
137 self.max_object_depth = depth;
138 self
139 }
140
141 pub fn max_operator_count(mut self, count: u64) -> Self {
143 self.max_operator_count = count;
144 self
145 }
146
147 pub fn max_image_pixels(mut self, pixels: u64) -> Self {
149 self.max_image_pixels = pixels;
150 self
151 }
152
153 pub fn max_xfa_nesting_depth(mut self, depth: u32) -> Self {
155 self.max_xfa_nesting_depth = depth;
156 self
157 }
158
159 pub fn max_formcalc_depth(mut self, depth: u32) -> Self {
161 self.max_formcalc_depth = depth;
162 self
163 }
164
165 pub fn check_file_size(&self, bytes: u64) -> Result<(), LimitError> {
167 if bytes > self.max_file_bytes {
168 Err(LimitError::FileTooLarge {
169 actual_bytes: bytes,
170 limit_bytes: self.max_file_bytes,
171 })
172 } else {
173 Ok(())
174 }
175 }
176
177 pub fn check_stream_size(&self, bytes: u64) -> Result<(), LimitError> {
179 if bytes > self.max_stream_bytes {
180 Err(LimitError::StreamTooLarge {
181 actual_bytes: bytes,
182 limit_bytes: self.max_stream_bytes,
183 })
184 } else {
185 Ok(())
186 }
187 }
188
189 pub fn check_image_pixels(&self, width: u64, height: u64) -> Result<(), LimitError> {
191 let pixels = width.saturating_mul(height);
192 if pixels > self.max_image_pixels {
193 Err(LimitError::ImageTooLarge {
194 width,
195 height,
196 pixels,
197 limit_pixels: self.max_image_pixels,
198 })
199 } else {
200 Ok(())
201 }
202 }
203
204 pub fn check_object_depth(&self, depth: u32) -> Result<(), LimitError> {
206 if depth > self.max_object_depth {
207 Err(LimitError::ObjectDepthExceeded {
208 depth,
209 limit: self.max_object_depth,
210 })
211 } else {
212 Ok(())
213 }
214 }
215}
216
217#[derive(Debug, Clone, PartialEq, Eq)]
219pub enum LimitError {
220 FileTooLarge { actual_bytes: u64, limit_bytes: u64 },
222 StreamTooLarge { actual_bytes: u64, limit_bytes: u64 },
224 ImageTooLarge {
226 width: u64,
227 height: u64,
228 pixels: u64,
229 limit_pixels: u64,
230 },
231 ObjectDepthExceeded { depth: u32, limit: u32 },
233 TooManyOperators { count: u64, limit: u64 },
235 XfaNestingTooDeep { depth: u32, limit: u32 },
237 FormCalcRecursionTooDeep { depth: u32, limit: u32 },
239}
240
241impl std::fmt::Display for LimitError {
242 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243 match self {
244 Self::FileTooLarge {
245 actual_bytes,
246 limit_bytes,
247 } => write!(
248 f,
249 "PDF file too large: {} MB (limit: {} MB)",
250 actual_bytes / 1024 / 1024,
251 limit_bytes / 1024 / 1024
252 ),
253 Self::StreamTooLarge {
254 actual_bytes,
255 limit_bytes,
256 } => write!(
257 f,
258 "Decompressed stream too large: {} MB (limit: {} MB)",
259 actual_bytes / 1024 / 1024,
260 limit_bytes / 1024 / 1024
261 ),
262 Self::ImageTooLarge {
263 width,
264 height,
265 pixels,
266 limit_pixels,
267 } => write!(
268 f,
269 "Image too large: {}×{} ({} MP, limit: {} MP)",
270 width,
271 height,
272 pixels / 1_000_000,
273 limit_pixels / 1_000_000
274 ),
275 Self::ObjectDepthExceeded { depth, limit } => write!(
276 f,
277 "Object reference depth exceeded: {} (limit: {})",
278 depth, limit
279 ),
280 Self::TooManyOperators { count, limit } => write!(
281 f,
282 "Content stream has too many operators: {} (limit: {})",
283 count, limit
284 ),
285 Self::XfaNestingTooDeep { depth, limit } => write!(
286 f,
287 "XFA template nesting too deep: {} (limit: {})",
288 depth, limit
289 ),
290 Self::FormCalcRecursionTooDeep { depth, limit } => write!(
291 f,
292 "FormCalc recursion too deep: {} (limit: {})",
293 depth, limit
294 ),
295 }
296 }
297}
298
299impl std::error::Error for LimitError {}
300
301#[cfg(test)]
302mod tests {
303 use super::*;
304
305 #[test]
306 fn test_default_limits() {
307 let l = ProcessingLimits::default();
308 assert_eq!(l.max_file_bytes, 500 * 1024 * 1024);
309 assert_eq!(l.max_stream_bytes, 256 * 1024 * 1024);
310 assert_eq!(l.max_image_pixels, 16384 * 16384);
311 }
312
313 #[test]
314 fn test_wasm_limits_stricter_than_default() {
315 let wasm = ProcessingLimits::wasm();
316 let default = ProcessingLimits::default();
317 assert!(wasm.max_file_bytes < default.max_file_bytes);
318 assert!(wasm.max_stream_bytes < default.max_stream_bytes);
319 assert!(wasm.max_image_pixels < default.max_image_pixels);
320 }
321
322 #[test]
323 fn test_file_size_check() {
324 let l = ProcessingLimits::default();
325 assert!(l.check_file_size(100 * 1024 * 1024).is_ok()); assert!(l.check_file_size(500 * 1024 * 1024).is_ok()); assert!(l.check_file_size(501 * 1024 * 1024).is_err()); }
329
330 #[test]
331 fn test_image_pixel_check() {
332 let l = ProcessingLimits::default();
333 assert!(l.check_image_pixels(1920, 1080).is_ok());
334 assert!(l.check_image_pixels(16384, 16384).is_ok()); assert!(l.check_image_pixels(16385, 16384).is_err()); }
337
338 #[test]
339 fn test_stream_size_check() {
340 let l = ProcessingLimits::default();
341 assert!(l.check_stream_size(100 * 1024 * 1024).is_ok());
342 assert!(l.check_stream_size(256 * 1024 * 1024).is_ok()); assert!(l.check_stream_size(257 * 1024 * 1024).is_err()); }
345
346 #[test]
347 fn test_builder_pattern() {
348 let l = ProcessingLimits::default()
349 .max_file_bytes(10 * 1024 * 1024)
350 .max_stream_bytes(5 * 1024 * 1024);
351 assert_eq!(l.max_file_bytes, 10 * 1024 * 1024);
352 assert_eq!(l.max_stream_bytes, 5 * 1024 * 1024);
353 }
354
355 #[test]
356 fn test_limit_error_display() {
357 let err = LimitError::FileTooLarge {
358 actual_bytes: 600 * 1024 * 1024,
359 limit_bytes: 500 * 1024 * 1024,
360 };
361 let msg = err.to_string();
362 assert!(msg.contains("600 MB"));
363 assert!(msg.contains("500 MB"));
364 }
365}