Skip to content

Commit afe79a4

Browse files
rthJ-F-Liu
authored andcommitted
refactor: add LoadOptions and Document::load_with_options
Introduce a LoadOptions struct (password, filter, strict) so loading configuration is extensible without new method variants. Existing load/load_from/load_mem methods delegate to new _with_options counterparts; password-only and filter-only shorthands are deprecated. The strict field is threaded to Reader but not yet acted on — a follow-up will use it to reject non-conforming PDF headers.
1 parent 855e569 commit afe79a4

File tree

7 files changed

+353
-42
lines changed

7 files changed

+353
-42
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
11

2+
## [Unreleased]
3+
4+
### Add
5+
6+
* Add `LoadOptions` struct with `password`, `filter`, and `strict` fields for extensible loading configuration
7+
* Add `load_with_options`, `load_from_with_options`, and `load_mem_with_options` methods (sync + async)
8+
9+
### Deprecate
10+
11+
* Deprecate `load_filtered`, `load_from_with_password`, `load_mem_with_password` in favor of `_with_options` variants
12+
213
<a name="v0.38.0"></a>
314
## [v0.38.0](https://github.com/J-F-Liu/lopdf/compare/v0.37.0...v0.38.0) (2025-08-26)
415

examples/extract_text.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::path::{Path, PathBuf};
66
use std::time::Instant;
77

88
use clap::Parser;
9-
use lopdf::{Document, Object};
9+
use lopdf::{Document, LoadOptions, Object};
1010
use rayon::iter::{IntoParallelIterator, ParallelIterator};
1111
use serde::{Deserialize, Serialize};
1212
use serde_json;
@@ -98,13 +98,14 @@ fn filter_func(object_id: (u32, u16), object: &mut Object) -> Option<((u32, u16)
9898

9999
#[cfg(not(feature = "async"))]
100100
fn load_pdf<P: AsRef<Path>>(path: P) -> Result<Document, Error> {
101-
Document::load_filtered(path, filter_func).map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
101+
Document::load_with_options(path, LoadOptions::with_filter(filter_func))
102+
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
102103
}
103104

104105
#[cfg(feature = "async")]
105106
fn load_pdf<P: AsRef<Path>>(path: P) -> Result<Document, Error> {
106107
Ok(Builder::new_current_thread().build().unwrap().block_on(async move {
107-
Document::load_filtered(path, filter_func)
108+
Document::load_with_options(path, LoadOptions::with_filter(filter_func))
108109
.await
109110
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
110111
})?)

examples/extract_toc.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
55
use std::time::Instant;
66

77
use clap::Parser;
8-
use lopdf::{Document, Object};
8+
use lopdf::{Document, LoadOptions, Object};
99
use serde_json;
1010
use shellexpand;
1111

@@ -89,13 +89,14 @@ fn filter_func(object_id: (u32, u16), object: &mut Object) -> Option<((u32, u16)
8989

9090
#[cfg(not(feature = "async"))]
9191
fn load_pdf<P: AsRef<Path>>(path: P) -> Result<Document, Error> {
92-
Document::load_filtered(path, filter_func).map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
92+
Document::load_with_options(path, LoadOptions::with_filter(filter_func))
93+
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
9394
}
9495

9596
#[cfg(feature = "async")]
9697
fn load_pdf<P: AsRef<Path>>(path: P) -> Result<Document, Error> {
9798
Ok(Builder::new_current_thread().build().unwrap().block_on(async move {
98-
Document::load_filtered(path, filter_func)
99+
Document::load_with_options(path, LoadOptions::with_filter(filter_func))
99100
.await
100101
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
101102
})?)

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ mod processor;
2626
mod toc;
2727
mod writer;
2828

29+
mod load_options;
2930
mod object_stream;
3031
mod parser;
3132
mod parser_aux;
@@ -46,6 +47,7 @@ pub use error::{Error, Result};
4647
pub use incremental_document::IncrementalDocument;
4748
pub use object_stream::{ObjectStream, ObjectStreamBuilder, ObjectStreamConfig};
4849
pub use outlines::Outline;
50+
pub use load_options::{FilterFunc, LoadOptions};
4951
pub use reader::{Reader, PdfMetadata};
5052
pub use save_options::{SaveOptions, SaveOptionsBuilder};
5153
pub use toc::Toc;

src/load_options.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use crate::Object;
2+
3+
/// Type alias for the filter function used during PDF loading.
4+
///
5+
/// The function receives an object ID and a mutable reference to the object,
6+
/// and returns `Some((id, object))` to keep it or `None` to discard it.
7+
pub type FilterFunc = fn((u32, u16), &mut Object) -> Option<((u32, u16), Object)>;
8+
9+
/// Options for loading PDF documents.
10+
///
11+
/// Use this struct to configure password, object filtering, and strictness
12+
/// when loading a PDF. The default is lenient parsing with no password or filter.
13+
///
14+
/// # Examples
15+
///
16+
/// ```no_run
17+
/// use lopdf::{Document, LoadOptions};
18+
///
19+
/// // Load with a password
20+
/// let doc = Document::load_with_options(
21+
/// "encrypted.pdf",
22+
/// LoadOptions::with_password("secret"),
23+
/// );
24+
///
25+
/// // Load with strict parsing
26+
/// let doc = Document::load_with_options(
27+
/// "document.pdf",
28+
/// LoadOptions { strict: true, ..Default::default() },
29+
/// );
30+
/// ```
31+
#[derive(Clone, Default)]
32+
pub struct LoadOptions {
33+
/// Password for encrypted PDFs.
34+
pub password: Option<String>,
35+
/// Object filter applied during loading.
36+
pub filter: Option<FilterFunc>,
37+
/// When `true`, reject non-conforming PDFs instead of silently accepting them.
38+
/// Defaults to `false` (lenient parsing).
39+
pub strict: bool,
40+
}
41+
42+
impl std::fmt::Debug for LoadOptions {
43+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44+
f.debug_struct("LoadOptions")
45+
.field("password", &self.password.as_ref().map(|_| "***"))
46+
.field("filter", &self.filter.map(|_| "fn(..)"))
47+
.field("strict", &self.strict)
48+
.finish()
49+
}
50+
}
51+
52+
impl LoadOptions {
53+
/// Create options with a password for encrypted PDFs.
54+
pub fn with_password(password: &str) -> Self {
55+
Self {
56+
password: Some(password.to_string()),
57+
..Default::default()
58+
}
59+
}
60+
61+
/// Create options with an object filter.
62+
pub fn with_filter(filter: FilterFunc) -> Self {
63+
Self {
64+
filter: Some(filter),
65+
..Default::default()
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)