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