1#![forbid(unsafe_code)]
2#![doc = "Core types and utilities for rpdfium — a faithful Rust port of PDFium."]
3pub mod bytestring;
16pub mod cfx_bitstream;
17pub mod constants;
18pub mod diagnostic;
19pub mod error;
20pub mod fx_bidi;
21pub mod fx_coordinates;
22pub mod fx_stream;
23pub mod fx_system;
24pub mod name;
25pub mod widestring;
26
27pub use bytestring::{PdfString, PdfStringEncoding, pdfdoc_to_char};
29pub use diagnostic::{DiagCategory, DiagLocation, Diagnostic, DiagnosticCollector, Severity};
30pub use error::{ObjectId, ParseError, PdfError, PdfResult};
31pub use fx_bidi::{
32 BidiChar, BidiDirection, BidiSegment, BidiString, Direction, Segment, bidi_class_of,
33 mirror_char,
34};
35pub use fx_coordinates::{Matrix, Point, Rect, RectI, Size, Vector2D};
36pub use fx_stream::{CountingWriter, MemoryStream, PdfSource, PdfWrite};
37pub use fx_system::is_float_zero;
38pub use name::Name;
39pub use widestring::WideString;
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
49pub enum ParsingMode {
50 Strict,
52 #[default]
54 Lenient,
55}
56
57impl std::fmt::Display for ParsingMode {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 match self {
60 ParsingMode::Strict => write!(f, "strict"),
61 ParsingMode::Lenient => write!(f, "lenient"),
62 }
63 }
64}
65
66#[derive(Debug, Clone)]
71pub struct OpenOptions {
72 pub parsing_mode: ParsingMode,
74
75 pub password: Option<String>,
77
78 pub max_decompressed_stream_size: u64,
82
83 pub max_compression_ratio: u32,
87
88 pub max_operators_per_page: u64,
90
91 pub max_endstream_scan_distance: u64,
93}
94
95impl OpenOptions {
96 pub fn with_parsing_mode(mut self, mode: ParsingMode) -> Self {
98 self.parsing_mode = mode;
99 self
100 }
101
102 pub fn with_password(mut self, password: impl Into<String>) -> Self {
104 self.password = Some(password.into());
105 self
106 }
107
108 pub fn with_max_decompressed_stream_size(mut self, size: u64) -> Self {
110 self.max_decompressed_stream_size = size;
111 self
112 }
113
114 pub fn with_max_compression_ratio(mut self, ratio: u32) -> Self {
116 self.max_compression_ratio = ratio;
117 self
118 }
119
120 pub fn with_max_operators_per_page(mut self, count: u64) -> Self {
122 self.max_operators_per_page = count;
123 self
124 }
125
126 pub fn with_max_endstream_scan_distance(mut self, distance: u64) -> Self {
128 self.max_endstream_scan_distance = distance;
129 self
130 }
131}
132
133impl Default for OpenOptions {
134 fn default() -> Self {
135 Self {
136 parsing_mode: ParsingMode::default(),
137 password: None,
138 max_decompressed_stream_size: fx_system::DEFAULT_MAX_DECOMPRESSED_STREAM_SIZE,
139 max_compression_ratio: fx_system::DEFAULT_MAX_COMPRESSION_RATIO,
140 max_operators_per_page: fx_system::DEFAULT_MAX_OPERATORS_PER_PAGE,
141 max_endstream_scan_distance: fx_system::DEFAULT_MAX_ENDSTREAM_SCAN_DISTANCE,
142 }
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 #[test]
151 fn test_parsing_mode_default_is_lenient() {
152 assert_eq!(ParsingMode::default(), ParsingMode::Lenient);
153 }
154
155 #[test]
156 fn test_parsing_mode_display() {
157 assert_eq!(format!("{}", ParsingMode::Strict), "strict");
158 assert_eq!(format!("{}", ParsingMode::Lenient), "lenient");
159 }
160
161 #[test]
162 fn test_open_options_default() {
163 let opts = OpenOptions::default();
164 assert_eq!(opts.parsing_mode, ParsingMode::Lenient);
165 assert!(opts.password.is_none());
166 assert_eq!(
167 opts.max_decompressed_stream_size,
168 fx_system::DEFAULT_MAX_DECOMPRESSED_STREAM_SIZE
169 );
170 assert_eq!(
171 opts.max_compression_ratio,
172 fx_system::DEFAULT_MAX_COMPRESSION_RATIO
173 );
174 assert_eq!(
175 opts.max_operators_per_page,
176 fx_system::DEFAULT_MAX_OPERATORS_PER_PAGE
177 );
178 assert_eq!(
179 opts.max_endstream_scan_distance,
180 fx_system::DEFAULT_MAX_ENDSTREAM_SCAN_DISTANCE
181 );
182 }
183
184 #[test]
185 fn test_open_options_custom() {
186 let opts = OpenOptions {
187 parsing_mode: ParsingMode::Strict,
188 password: Some("secret".to_string()),
189 max_decompressed_stream_size: 512 * 1024 * 1024,
190 ..OpenOptions::default()
191 };
192 assert_eq!(opts.parsing_mode, ParsingMode::Strict);
193 assert_eq!(opts.password.as_deref(), Some("secret"));
194 assert_eq!(opts.max_decompressed_stream_size, 512 * 1024 * 1024);
195 }
196
197 #[test]
198 fn test_open_options_builder_parsing_mode() {
199 let opts = OpenOptions::default().with_parsing_mode(ParsingMode::Strict);
200 assert_eq!(opts.parsing_mode, ParsingMode::Strict);
201 }
202
203 #[test]
204 fn test_open_options_builder_password() {
205 let opts = OpenOptions::default().with_password("secret");
206 assert_eq!(opts.password.as_deref(), Some("secret"));
207 }
208
209 #[test]
210 fn test_open_options_builder_max_decompressed_stream_size() {
211 let opts = OpenOptions::default().with_max_decompressed_stream_size(1024);
212 assert_eq!(opts.max_decompressed_stream_size, 1024);
213 }
214
215 #[test]
216 fn test_open_options_builder_max_compression_ratio() {
217 let opts = OpenOptions::default().with_max_compression_ratio(500);
218 assert_eq!(opts.max_compression_ratio, 500);
219 }
220
221 #[test]
222 fn test_open_options_builder_max_operators() {
223 let opts = OpenOptions::default().with_max_operators_per_page(5000);
224 assert_eq!(opts.max_operators_per_page, 5000);
225 }
226
227 #[test]
228 fn test_open_options_builder_max_endstream_scan() {
229 let opts = OpenOptions::default().with_max_endstream_scan_distance(8192);
230 assert_eq!(opts.max_endstream_scan_distance, 8192);
231 }
232
233 #[test]
234 fn test_open_options_builder_chaining() {
235 let opts = OpenOptions::default()
236 .with_parsing_mode(ParsingMode::Strict)
237 .with_password("pass")
238 .with_max_decompressed_stream_size(512)
239 .with_max_compression_ratio(100)
240 .with_max_operators_per_page(1000)
241 .with_max_endstream_scan_distance(4096);
242 assert_eq!(opts.parsing_mode, ParsingMode::Strict);
243 assert_eq!(opts.password.as_deref(), Some("pass"));
244 assert_eq!(opts.max_decompressed_stream_size, 512);
245 assert_eq!(opts.max_compression_ratio, 100);
246 assert_eq!(opts.max_operators_per_page, 1000);
247 assert_eq!(opts.max_endstream_scan_distance, 4096);
248 }
249
250 #[test]
251 fn test_parsing_mode_is_send_sync() {
252 fn assert_send_sync<T: Send + Sync>() {}
253 assert_send_sync::<ParsingMode>();
254 }
255
256 #[test]
257 fn test_open_options_is_send_sync() {
258 fn assert_send_sync<T: Send + Sync>() {}
259 assert_send_sync::<OpenOptions>();
260 }
261
262 #[test]
263 fn test_parsing_mode_equality() {
264 assert_eq!(ParsingMode::Strict, ParsingMode::Strict);
265 assert_eq!(ParsingMode::Lenient, ParsingMode::Lenient);
266 assert_ne!(ParsingMode::Strict, ParsingMode::Lenient);
267 }
268
269 #[test]
270 fn test_all_core_types_reexported() {
271 let _: ObjectId = ObjectId {
273 number: 1,
274 generation: 0,
275 };
276 let _: PdfString = PdfString::from_bytes(vec![]);
277 let _: Name = Name::from("Test");
278 let _: Point = Point::new(0.0, 0.0);
279 let _: Size = Size::new(0.0, 0.0);
280 let _: Rect = Rect::new(0.0, 0.0, 1.0, 1.0);
281 let _: Matrix = Matrix::identity();
282 let _: ParsingMode = ParsingMode::default();
283 let _: OpenOptions = OpenOptions::default();
284 let _: DiagnosticCollector = DiagnosticCollector::new();
285 }
286}