1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
//! Error types for the crate.
use std::fmt;
use std::path::PathBuf;
/// Convenience alias used throughout the crate.
pub type Result<T> = std::result::Result<T, Error>;
/// Failure to locate, open, or validate the PDFium shared library.
#[derive(Debug)]
#[non_exhaustive]
pub enum LoadError {
/// No candidate library was found. Contains every location that was
/// tried, in discovery order.
LibraryNotFound {
/// Every location tried, in discovery order.
searched: Vec<String>,
},
/// A concrete library file was found (or explicitly given) but the
/// dynamic loader failed to open it.
OpenFailed {
/// The library file that failed to open.
path: PathBuf,
/// The dynamic loader's error.
source: libloading::Error,
},
/// The library opened, but a required PDFium symbol is missing — the
/// build is older than the oldest version this crate supports, or the
/// file is not PDFium at all.
MissingSymbol(crate::sys::MissingSymbolError),
}
impl fmt::Display for LoadError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LoadError::LibraryNotFound { searched } => {
write!(
f,
"PDFium shared library not found; searched: {}. \
Run `cargo xtask fetch-pdfium`, set PDFIUM_LIB_PATH, or use \
Pdfium::load_from_path()",
searched.join(", ")
)
}
LoadError::OpenFailed { path, source } => {
write!(
f,
"failed to open PDFium library at {}: {source}",
path.display()
)
}
LoadError::MissingSymbol(e) => write!(f, "{e}"),
}
}
}
impl std::error::Error for LoadError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
LoadError::LibraryNotFound { .. } => None,
LoadError::OpenFailed { source, .. } => Some(source),
LoadError::MissingSymbol(e) => Some(e),
}
}
}
/// All errors returned by the safe API.
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
/// The PDFium library could not be loaded.
Load(LoadError),
/// PDFium is already loaded from a different library path; the process
/// keeps the first successfully loaded library for its lifetime.
AlreadyLoaded {
/// Where the active instance was loaded from (`None` when it was
/// resolved by bare name through the system loader).
loaded_from: Option<PathBuf>,
/// The conflicting path passed to this call.
requested: PathBuf,
},
/// I/O failure while reading a PDF file from disk.
Io(std::io::Error),
/// The document is encrypted and requires a password, but none was
/// supplied.
PasswordRequired,
/// A password was supplied but does not unlock the document.
IncorrectPassword,
/// The document uses a security/encryption scheme PDFium does not
/// support.
UnsupportedSecurity,
/// The data is not a PDF, or is too corrupt to open.
InvalidPdf,
/// PDFium reported an error code this crate does not recognize.
Pdfium {
/// Raw `FPDF_GetLastError` value.
code: u64,
},
/// Requested page index does not exist.
PageIndexOutOfBounds {
/// The requested 0-based page index.
index: usize,
/// The document's page count.
count: usize,
},
/// PDFium failed to load a page that should exist (severely corrupt
/// page tree or content).
PageLoadFailed {
/// The 0-based page index that failed to load.
index: usize,
},
/// PDFium failed to prepare the page for text extraction.
TextLoadFailed {
/// The 0-based page index whose text failed to load.
index: usize,
},
/// The page reports more text characters than the configured
/// extraction limit (see [`PdfPage::text_with_limit`]); nothing was
/// allocated.
///
/// [`PdfPage::text_with_limit`]: crate::PdfPage::text_with_limit
TextTooLarge {
/// Characters PDFium reports on the page.
chars: usize,
/// The configured ceiling.
limit: usize,
},
/// PDFium failed to initialize the form-fill environment.
FormInitFailed,
/// The rendered output would exceed [`RenderConfig::max_output_bytes`]
/// (or the hard `i32` pixel-dimension limits of PDFium's bitmap API).
///
/// [`RenderConfig::max_output_bytes`]: crate::RenderConfig::max_output_bytes
RenderTooLarge {
/// Bytes the requested output would need.
required_bytes: u64,
/// The configured ceiling.
limit: u64,
},
/// Rendering failed inside PDFium (bitmap creation or coordinate
/// transform rejected).
RenderFailed {
/// Which PDFium operation rejected the render.
reason: &'static str,
},
/// An argument or configuration value is invalid (zero/non-finite
/// scale, zero target dimensions, interior NUL byte in a password, ...).
InvalidConfig(String),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Load(e) => write!(f, "{e}"),
Error::AlreadyLoaded {
loaded_from,
requested,
} => match loaded_from {
Some(p) => write!(
f,
"PDFium is already loaded from {} (requested {}); a process keeps \
its first PDFium library for its lifetime",
p.display(),
requested.display()
),
None => write!(
f,
"PDFium is already loaded via the system loader (requested {}); a \
process keeps its first PDFium library for its lifetime",
requested.display()
),
},
Error::Io(e) => write!(f, "I/O error reading PDF: {e}"),
Error::PasswordRequired => {
write!(f, "document is encrypted and requires a password")
}
Error::IncorrectPassword => write!(f, "incorrect password for encrypted document"),
Error::UnsupportedSecurity => {
write!(f, "document uses an unsupported security scheme")
}
Error::InvalidPdf => write!(f, "data is not a valid PDF document"),
Error::Pdfium { code } => write!(f, "PDFium error code {code}"),
Error::PageIndexOutOfBounds { index, count } => {
write!(
f,
"page index {index} out of bounds (document has {count} pages)"
)
}
Error::PageLoadFailed { index } => write!(f, "PDFium failed to load page {index}"),
Error::TextLoadFailed { index } => {
write!(f, "PDFium failed to load text for page {index}")
}
Error::TextTooLarge { chars, limit } => write!(
f,
"page reports {chars} text characters, exceeding the extraction \
limit of {limit}"
),
Error::FormInitFailed => {
write!(f, "PDFium failed to initialize the form-fill environment")
}
Error::RenderTooLarge {
required_bytes,
limit,
} => write!(
f,
"rendered output would require {required_bytes} bytes, exceeding the \
configured limit of {limit} bytes"
),
Error::RenderFailed { reason } => write!(f, "rendering failed: {reason}"),
Error::InvalidConfig(msg) => write!(f, "invalid configuration: {msg}"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Load(e) => Some(e),
Error::Io(e) => Some(e),
_ => None,
}
}
}
impl From<LoadError> for Error {
fn from(e: LoadError) -> Self {
Error::Load(e)
}
}
impl Error {
/// True when the failure is specifically about encryption/passwords:
/// [`Error::PasswordRequired`], [`Error::IncorrectPassword`], or
/// [`Error::UnsupportedSecurity`].
pub fn is_encryption_error(&self) -> bool {
matches!(
self,
Error::PasswordRequired | Error::IncorrectPassword | Error::UnsupportedSecurity
)
}
}