pdfium_render/pdfium.rs
1//! Defines the [Pdfium] struct, a high-level idiomatic Rust wrapper around Pdfium.
2
3use crate::bindgen::{
4 FPDF_DOCUMENT, FPDF_ERR_FILE, FPDF_ERR_FORMAT, FPDF_ERR_PAGE, FPDF_ERR_PASSWORD,
5 FPDF_ERR_SECURITY, FPDF_ERR_SUCCESS, FPDF_ERR_UNKNOWN,
6};
7use crate::bindings::PdfiumLibraryBindings;
8use crate::config::PdfiumLibraryConfig;
9use crate::error::{PdfiumError, PdfiumInternalError};
10use crate::pdf::document::{PdfDocument, PdfDocumentVersion};
11use crate::pdf::font::provider::{PdfiumCustomFontProvider, PdfiumCustomFontProviderExt};
12use once_cell::sync::OnceCell;
13use std::fmt::{Debug, Formatter};
14use std::pin::Pin;
15
16#[cfg(all(not(target_arch = "wasm32"), not(feature = "static")))]
17use {
18 crate::bindings::dynamic_bindings::DynamicPdfiumBindings, libloading::Library,
19 std::ffi::OsString, std::path::PathBuf,
20};
21
22#[cfg(all(not(target_arch = "wasm32"), feature = "static"))]
23use crate::bindings::static_bindings::StaticPdfiumBindings;
24
25#[cfg(not(target_arch = "wasm32"))]
26use {
27 crate::bindgen::FPDF_SYSFONTINFO,
28 crate::utils::files::get_pdfium_file_accessor_from_reader,
29 std::fs::File,
30 std::io::{Read, Seek},
31 std::path::Path,
32};
33
34#[cfg(target_arch = "wasm32")]
35use {
36 crate::bindings::wasm_bindings::{PdfiumRenderWasmState, WasmPdfiumBindings},
37 js_sys::{ArrayBuffer, Uint8Array},
38 wasm_bindgen::JsCast,
39 wasm_bindgen_futures::JsFuture,
40 web_sys::{window, Blob, Response},
41};
42
43#[cfg(feature = "thread_safe")]
44use crate::bindings::thread_safe::ThreadSafePdfiumBindings;
45
46// The following dummy declaration is used only when running cargo doc.
47// It allows documentation of WASM-specific functionality to be included
48// in documentation generated on non-WASM targets.
49#[cfg(doc)]
50struct Blob;
51
52#[cfg(all(not(target_arch = "wasm32"), feature = "thread_safe"))]
53/// The trait bound for a thread-safe reader passed to [Pdfium::load_pdf_from_reader].
54pub trait PdfiumReader: Read + Seek + Send {}
55
56#[cfg(all(not(target_arch = "wasm32"), feature = "thread_safe"))]
57impl<R: Read + Seek + Send> PdfiumReader for R {}
58
59#[cfg(all(not(target_arch = "wasm32"), not(feature = "thread_safe")))]
60/// The trait bound for a non-thread-safe reader passed to [Pdfium::load_pdf_from_reader].
61pub trait PdfiumReader: Read + Seek {}
62
63#[cfg(all(not(target_arch = "wasm32"), not(feature = "thread_safe")))]
64impl<R: Read + Seek> PdfiumReader for R {}
65
66// The first instantiation of a Pdfium object will promote a concrete PdfiumLibraryBindings
67// trait implementation into a global static OnceCell. This allows for thread-safe,
68// lifetime-free access to that PdfiumLibraryBindings instance from any object that
69// implements the PdfiumLibraryBindingsAccessor trait.
70static BINDINGS: OnceCell<Box<dyn PdfiumLibraryBindings>> = OnceCell::new();
71
72#[cfg(feature = "thread_safe")]
73pub(crate) trait PdfiumLibraryBindingsAccessor<'a>: Send + Sync {
74 fn bindings(&self) -> &'a dyn PdfiumLibraryBindings {
75 BINDINGS.wait().as_ref()
76 }
77}
78
79#[cfg(not(feature = "thread_safe"))]
80pub(crate) trait PdfiumLibraryBindingsAccessor<'a> {
81 fn bindings(&self) -> &'a dyn PdfiumLibraryBindings {
82 BINDINGS.get().unwrap().as_ref()
83 }
84}
85
86/// A high-level idiomatic Rust wrapper around Pdfium, the C++ PDF library used by
87/// the Google Chromium project.
88pub struct Pdfium {
89 pub(crate) custom_font_provider: Option<Pin<Box<PdfiumCustomFontProviderExt>>>,
90
91 #[cfg(not(target_arch = "wasm32"))]
92 pub(crate) platform_default_font_provider: Option<*mut FPDF_SYSFONTINFO>,
93}
94
95impl Pdfium {
96 #[cfg(not(target_arch = "wasm32"))]
97 #[cfg(any(doc, feature = "static"))]
98 /// Binds to a Pdfium library that was statically linked into the currently running
99 /// executable, returning a new [PdfiumLibraryBindings] object that contains bindings to the
100 /// functions exposed by the library. The application will immediately crash if Pdfium
101 /// was not correctly statically linked into the executable at compile time.
102 ///
103 /// This function is only available when this crate's `static` feature is enabled.
104 #[inline]
105 pub fn bind_to_statically_linked_library() -> Result<Box<dyn PdfiumLibraryBindings>, PdfiumError>
106 {
107 if BINDINGS.get().is_none() {
108 let bindings = StaticPdfiumBindings::new();
109
110 #[cfg(feature = "thread_safe")]
111 let bindings = ThreadSafePdfiumBindings::new(bindings);
112
113 Ok(Box::new(bindings))
114 } else {
115 Err(PdfiumError::PdfiumLibraryBindingsAlreadyInitialized)
116 }
117 }
118
119 #[cfg(not(target_arch = "wasm32"))]
120 #[cfg(not(feature = "static"))]
121 /// Initializes the external Pdfium library, loading it from the system libraries.
122 /// Returns a new [PdfiumLibraryBindings] object that contains bindings to the functions exposed
123 /// by the library, or an error if the library could not be loaded.
124 #[inline]
125 pub fn bind_to_system_library() -> Result<Box<dyn PdfiumLibraryBindings>, PdfiumError> {
126 if BINDINGS.get().is_none() {
127 let bindings = DynamicPdfiumBindings::new(
128 unsafe { Library::new(Self::pdfium_platform_library_name()) }
129 .map_err(PdfiumError::LoadLibraryError)?,
130 )?;
131
132 #[cfg(feature = "thread_safe")]
133 let bindings = ThreadSafePdfiumBindings::new(bindings);
134
135 Ok(Box::new(bindings))
136 } else {
137 Err(PdfiumError::PdfiumLibraryBindingsAlreadyInitialized)
138 }
139 }
140
141 #[cfg(target_arch = "wasm32")]
142 /// Initializes the external Pdfium library, binding to an external WASM module.
143 /// Returns a new [PdfiumLibraryBindings] object that contains bindings to the functions exposed
144 /// by the library, or an error if the library is not available.
145 ///
146 /// It is essential that the exported `initialize_pdfium_render()` function be called
147 /// from Javascript _before_ calling this function from within your Rust code. For an example, see:
148 /// <https://github.com/ajrcarey/pdfium-render/blob/master/examples/index.html>
149 #[inline]
150 pub fn bind_to_system_library() -> Result<Box<dyn PdfiumLibraryBindings>, PdfiumError> {
151 if BINDINGS.get().is_none() {
152 if PdfiumRenderWasmState::lock().is_ready() {
153 let bindings = WasmPdfiumBindings::new();
154
155 #[cfg(feature = "thread_safe")]
156 let bindings = ThreadSafePdfiumBindings::new(bindings);
157
158 Ok(Box::new(bindings))
159 } else {
160 Err(PdfiumError::PdfiumWasmModuleNotInitialized)
161 }
162 } else {
163 Err(PdfiumError::PdfiumLibraryBindingsAlreadyInitialized)
164 }
165 }
166
167 #[cfg(not(target_arch = "wasm32"))]
168 #[cfg(not(feature = "static"))]
169 /// Initializes the external pdfium library, loading it from the given path.
170 /// Returns a new [PdfiumLibraryBindings] object that contains bindings to the functions
171 /// exposed by the library, or an error if the library could not be loaded.
172 #[inline]
173 pub fn bind_to_library(
174 path: impl AsRef<Path>,
175 ) -> Result<Box<dyn PdfiumLibraryBindings>, PdfiumError> {
176 if BINDINGS.get().is_none() {
177 let bindings = DynamicPdfiumBindings::new(
178 unsafe { Library::new(path.as_ref().as_os_str()) }
179 .map_err(PdfiumError::LoadLibraryError)?,
180 )?;
181
182 #[cfg(feature = "thread_safe")]
183 let bindings = ThreadSafePdfiumBindings::new(bindings);
184
185 Ok(Box::new(bindings))
186 } else {
187 Err(PdfiumError::PdfiumLibraryBindingsAlreadyInitialized)
188 }
189 }
190
191 #[cfg(not(target_arch = "wasm32"))]
192 #[cfg(not(feature = "static"))]
193 /// Returns the name of the external Pdfium library on the currently running platform.
194 /// On Linux and Android, this will be `libpdfium.so` or similar; on Windows, this will
195 /// be `pdfium.dll` or similar; on MacOS, this will be `libpdfium.dylib` or similar.
196 #[inline]
197 pub fn pdfium_platform_library_name() -> OsString {
198 libloading::library_filename("pdfium")
199 }
200
201 #[cfg(not(target_arch = "wasm32"))]
202 #[cfg(not(feature = "static"))]
203 /// Returns the name of the external Pdfium library on the currently running platform,
204 /// prefixed with the given path string.
205 #[inline]
206 pub fn pdfium_platform_library_name_at_path(path: &(impl AsRef<Path> + ?Sized)) -> PathBuf {
207 path.as_ref().join(Pdfium::pdfium_platform_library_name())
208 }
209
210 /// Creates a new [Pdfium] instance from the given external Pdfium library bindings.
211 #[inline]
212 pub fn new(bindings: Box<dyn PdfiumLibraryBindings>) -> Self {
213 BINDINGS.get_or_init(move || {
214 unsafe {
215 bindings.FPDF_InitLibrary();
216 }
217
218 bindings
219 });
220
221 Self {
222 custom_font_provider: None,
223
224 #[cfg(not(target_arch = "wasm32"))]
225 platform_default_font_provider: None,
226 }
227 }
228
229 /// Creates a new [Pdfium] instance from the given external Pdfium library bindings,
230 /// using the custom library configuration in the given [PdfiumLibraryConfig].
231 #[inline]
232 pub fn new_with_config(
233 bindings: Box<dyn PdfiumLibraryBindings>,
234 mut config: PdfiumLibraryConfig,
235 ) -> Self {
236 BINDINGS.get_or_init(move || {
237 unsafe {
238 bindings.FPDF_InitLibraryWithConfig(&config.as_pdfium());
239 }
240
241 bindings
242 });
243
244 Self {
245 custom_font_provider: None,
246
247 #[cfg(not(target_arch = "wasm32"))]
248 platform_default_font_provider: None,
249 }
250 }
251
252 /// Applies the given custom font provider to this [Pdfium] instance.
253 ///
254 /// If the given custom font provider implementation itself calls Pdfium functions,
255 /// then it will block when used in conjunction with this crate's `thread_safe` feature.
256 pub fn set_custom_font_provider(&mut self, provider: Box<dyn PdfiumCustomFontProvider>) {
257 let mut wrapper = Box::pin(PdfiumCustomFontProviderExt::new(provider));
258
259 unsafe {
260 self.bindings()
261 .FPDF_SetSystemFontInfo(wrapper.as_fpdf_sys_font_info_mut_ptr());
262 }
263
264 self.custom_font_provider = Some(wrapper);
265 }
266
267 /// Clears the currently set font provider, including Pdfium's platform default font provider.
268 pub fn clear_custom_font_provider(&mut self) {
269 unsafe {
270 self.bindings().FPDF_SetSystemFontInfo(std::ptr::null_mut());
271 }
272
273 self.custom_font_provider = None;
274 }
275
276 #[cfg(not(target_arch = "wasm32"))]
277 /// Applies Pdfium's included default font provider for the current platform, if any,
278 /// to this [Pdfium] instance.
279 pub fn use_platform_default_font_provider(&mut self) -> Result<(), PdfiumError> {
280 self.clear_custom_font_provider();
281
282 let platform_default_font_provider =
283 unsafe { self.bindings().FPDF_GetDefaultSystemFontInfo() };
284
285 if !platform_default_font_provider.is_null() {
286 unsafe {
287 self.bindings()
288 .FPDF_SetSystemFontInfo(platform_default_font_provider);
289 }
290
291 self.platform_default_font_provider = Some(platform_default_font_provider);
292
293 Ok(())
294 } else {
295 Err(PdfiumError::NoPlatformDefaultFontProvider)
296 }
297 }
298
299 #[cfg(target_arch = "wasm32")]
300 /// Applies Pdfium's included default font provider for the current platform, if any,
301 /// to this [Pdfium] instance.
302 ///
303 /// This function will always return a `PdfiumError::NoPlatformDefaultFontProvider` error
304 /// when compiling to WASM, because Pdfium does not include a default platform provider
305 /// implementation for WASM.
306 pub fn use_platform_default_font_provider(&mut self) -> Result<(), PdfiumError> {
307 Err(PdfiumError::NoPlatformDefaultFontProvider)
308 }
309
310 /// Attempts to open a [PdfDocument] from the given static byte buffer.
311 ///
312 /// If the document is password protected, the given password will be used to unlock it.
313 pub fn load_pdf_from_byte_slice<'a>(
314 &'a self,
315 bytes: &'a [u8],
316 password: Option<&str>,
317 ) -> Result<PdfDocument<'a>, PdfiumError> {
318 Self::pdfium_document_handle_to_result(
319 unsafe { self.bindings().FPDF_LoadMemDocument64(bytes, password) },
320 self.bindings(),
321 )
322 }
323
324 /// Attempts to open a [PdfDocument] from the given owned byte buffer.
325 ///
326 /// If the document is password protected, the given password will be used to unlock it.
327 ///
328 /// `pdfium-render` will take ownership of the given byte buffer, ensuring its lifetime lasts
329 /// as long as the [PdfDocument] opened from it.
330 pub fn load_pdf_from_byte_vec(
331 &self,
332 bytes: Vec<u8>,
333 password: Option<&str>,
334 ) -> Result<PdfDocument<'_>, PdfiumError> {
335 Self::pdfium_document_handle_to_result(
336 unsafe {
337 self.bindings()
338 .FPDF_LoadMemDocument64(bytes.as_slice(), password)
339 },
340 self.bindings(),
341 )
342 .map(|mut document| {
343 // Give the newly-created document ownership of the byte buffer, so that Pdfium can continue
344 // to read from it on an as-needed basis throughout the lifetime of the document.
345
346 document.set_source_byte_buffer(bytes);
347
348 document
349 })
350 }
351
352 #[cfg(not(target_arch = "wasm32"))]
353 /// Attempts to open a [PdfDocument] from the given file path.
354 ///
355 /// If the document is password protected, the given password will be used
356 /// to unlock it.
357 ///
358 /// This function is not available when compiling to WASM. You have several options for
359 /// loading your PDF document data in WASM:
360 /// * Use the [Pdfium::load_pdf_from_fetch()] function to download document data from a
361 /// URL using the browser's built-in `fetch` API. This function is only available when
362 /// compiling to WASM.
363 /// * Use the [Pdfium::load_pdf_from_blob()] function to load document data from a
364 /// Javascript `File` or `Blob` object (such as a `File` object returned from an HTML
365 /// `<input type="file">` element). This function is only available when compiling to WASM.
366 /// * Use another method to retrieve the bytes of the target document over the network,
367 /// then load those bytes into Pdfium using either the [Pdfium::load_pdf_from_byte_slice()]
368 /// function or the [Pdfium::load_pdf_from_byte_vec()] function.
369 /// * Embed the bytes of the target document directly into the compiled WASM module
370 /// using the `include_bytes!` macro.
371 pub fn load_pdf_from_file<'a>(
372 &'a self,
373 path: &(impl AsRef<Path> + ?Sized),
374 password: Option<&str>,
375 ) -> Result<PdfDocument<'a>, PdfiumError> {
376 self.load_pdf_from_reader(File::open(path).map_err(PdfiumError::IoError)?, password)
377 }
378
379 #[cfg(not(target_arch = "wasm32"))]
380 /// Attempts to open a [PdfDocument] from the given reader.
381 ///
382 /// Pdfium will only load the portions of the document it actually needs into memory.
383 /// This is more efficient than loading the entire document into memory, especially when
384 /// working with large documents, and allows for working with documents larger than the
385 /// amount of available memory.
386 ///
387 /// Because Pdfium must know the total content length in advance prior to loading
388 /// any portion of it, the given reader must implement the [Seek] trait as well as
389 /// the [Read] trait.
390 ///
391 /// If the given reader implementation itself calls Pdfium functions, then it will block
392 /// when used in conjunction with this crate's `thread_safe` feature.
393 ///
394 /// If the document is password protected, the given password will be used
395 /// to unlock it.
396 ///
397 /// This function is not available when compiling to WASM. You have several options for
398 /// loading your PDF document data in WASM:
399 /// * Use the [Pdfium::load_pdf_from_fetch()] function to download document data from a
400 /// URL using the browser's built-in `fetch` API. This function is only available when
401 /// compiling to WASM.
402 /// * Use the [Pdfium::load_pdf_from_blob()] function to load document data from a
403 /// Javascript `File` or `Blob` object (such as a `File` object returned from an HTML
404 /// `<input type="file">` element). This function is only available when compiling to WASM.
405 /// * Use another method to retrieve the bytes of the target document over the network,
406 /// then load those bytes into Pdfium using either the [Pdfium::load_pdf_from_byte_slice()]
407 /// function or the [Pdfium::load_pdf_from_byte_vec()] function.
408 /// * Embed the bytes of the target document directly into the compiled WASM module
409 /// using the `include_bytes!` macro.
410 pub fn load_pdf_from_reader<'a, R: PdfiumReader + 'a>(
411 &'a self,
412 reader: R,
413 password: Option<&str>,
414 ) -> Result<PdfDocument<'a>, PdfiumError> {
415 let mut reader = get_pdfium_file_accessor_from_reader(reader);
416
417 Pdfium::pdfium_document_handle_to_result(
418 unsafe {
419 self.bindings()
420 .FPDF_LoadCustomDocument(reader.as_fpdf_file_access_mut_ptr(), password)
421 },
422 self.bindings(),
423 )
424 .map(|mut document| {
425 // Give the newly-created document ownership of the reader, so that Pdfium can continue
426 // to read from it on an as-needed basis throughout the lifetime of the document.
427
428 document.set_file_access_reader(reader);
429
430 document
431 })
432 }
433
434 #[cfg(any(doc, target_arch = "wasm32"))]
435 /// Attempts to open a [PdfDocument] by loading document data from the given URL.
436 /// The Javascript `fetch` API is used to download data over the network.
437 ///
438 /// If the document is password protected, the given password will be used to unlock it.
439 ///
440 /// This function is only available when compiling to WASM.
441 pub async fn load_pdf_from_fetch<'a>(
442 &'a self,
443 url: impl ToString,
444 password: Option<&str>,
445 ) -> Result<PdfDocument<'a>, PdfiumError> {
446 if let Some(window) = window() {
447 let fetch_result = JsFuture::from(window.fetch_with_str(url.to_string().as_str()))
448 .await
449 .map_err(PdfiumError::WebSysFetchError)?;
450
451 debug_assert!(fetch_result.is_instance_of::<Response>());
452
453 let response: Response = fetch_result
454 .dyn_into()
455 .map_err(|_| PdfiumError::WebSysInvalidResponseError)?;
456
457 let blob: Blob =
458 JsFuture::from(response.blob().map_err(PdfiumError::WebSysFetchError)?)
459 .await
460 .map_err(PdfiumError::WebSysFetchError)?
461 .into();
462
463 self.load_pdf_from_blob(blob, password).await
464 } else {
465 Err(PdfiumError::WebSysWindowObjectNotAvailable)
466 }
467 }
468
469 #[cfg(any(doc, target_arch = "wasm32"))]
470 /// Attempts to open a [PdfDocument] by loading document data from the given `Blob`.
471 /// A `File` object returned from a `FileList` is a suitable `Blob`:
472 ///
473 /// ```text
474 /// <input id="filePicker" type="file">
475 ///
476 /// const file = document.getElementById('filePicker').files[0];
477 /// ```
478 ///
479 /// If the document is password protected, the given password will be used to unlock it.
480 ///
481 /// This function is only available when compiling to WASM.
482 pub async fn load_pdf_from_blob<'a>(
483 &'a self,
484 blob: Blob,
485 password: Option<&str>,
486 ) -> Result<PdfDocument<'a>, PdfiumError> {
487 let array_buffer: ArrayBuffer = JsFuture::from(blob.array_buffer())
488 .await
489 .map_err(PdfiumError::WebSysFetchError)?
490 .into();
491
492 let u8_array: Uint8Array = Uint8Array::new(&array_buffer);
493
494 let bytes: Vec<u8> = u8_array.to_vec();
495
496 self.load_pdf_from_byte_vec(bytes, password)
497 }
498
499 /// Creates a new, empty [PdfDocument] in memory.
500 pub fn create_new_pdf<'a>(&'a self) -> Result<PdfDocument<'a>, PdfiumError> {
501 Self::pdfium_document_handle_to_result(
502 unsafe { self.bindings().FPDF_CreateNewDocument() },
503 self.bindings(),
504 )
505 .map(|mut document| {
506 document.set_version(PdfDocumentVersion::DEFAULT_VERSION);
507
508 document
509 })
510 }
511
512 /// Returns a [PdfDocument] from the given `FPDF_DOCUMENT` handle, if possible.
513 pub(crate) fn pdfium_document_handle_to_result(
514 handle: FPDF_DOCUMENT,
515 bindings: &dyn PdfiumLibraryBindings,
516 ) -> Result<PdfDocument<'_>, PdfiumError> {
517 if handle.is_null() {
518 // Retrieve the error code of the last error recorded by Pdfium.
519
520 if let Some(error) = match unsafe { bindings.FPDF_GetLastError() } as u32 {
521 FPDF_ERR_SUCCESS => None,
522 FPDF_ERR_UNKNOWN => Some(PdfiumInternalError::Unknown),
523 FPDF_ERR_FILE => Some(PdfiumInternalError::FileError),
524 FPDF_ERR_FORMAT => Some(PdfiumInternalError::FormatError),
525 FPDF_ERR_PASSWORD => Some(PdfiumInternalError::PasswordError),
526 FPDF_ERR_SECURITY => Some(PdfiumInternalError::SecurityError),
527 FPDF_ERR_PAGE => Some(PdfiumInternalError::PageError),
528 // The Pdfium documentation says "... if the previous SDK call succeeded, [then] the
529 // return value of this function is not defined". On Linux, at least, a return value
530 // of FPDF_ERR_SUCCESS seems to be consistently returned; on Windows, however, the
531 // return values are indeed unpredictable. See https://github.com/ajrcarey/pdfium-render/issues/24.
532 // Therefore, if the return value does not match one of the FPDF_ERR_* constants, we must
533 // assume success.
534 _ => None,
535 } {
536 Err(PdfiumError::PdfiumLibraryInternalError(error))
537 } else {
538 // This would be an unusual situation; a null handle indicating failure,
539 // yet Pdfium's error code indicates success.
540
541 Err(PdfiumError::PdfiumLibraryInternalError(
542 PdfiumInternalError::Unknown,
543 ))
544 }
545 } else {
546 Ok(PdfDocument::from_pdfium(handle))
547 }
548 }
549}
550
551impl Default for Pdfium {
552 #[cfg(feature = "static")]
553 /// Binds to a Pdfium library that was statically linked into the currently running
554 /// executable by calling [Pdfium::bind_to_statically_linked_library]. This function
555 /// will panic if no statically linked Pdfium functions can be located.
556 #[inline]
557 fn default() -> Self {
558 Pdfium::new(Pdfium::bind_to_statically_linked_library().expect("No Pdfium library found"))
559 }
560
561 #[cfg(not(feature = "static"))]
562 #[cfg(not(target_arch = "wasm32"))]
563 /// Binds to an external Pdfium library by first attempting to bind to a Pdfium library
564 /// in the current working directory; if that fails, then a system-provided library
565 /// will be used as a fall back.
566 ///
567 /// This function will panic if no suitable Pdfium library can be loaded.
568 #[inline]
569 fn default() -> Self {
570 // Attempt to bind to a Pdfium library in the current working directory.
571
572 match Pdfium::bind_to_library(Pdfium::pdfium_platform_library_name_at_path("./")) {
573 Ok(bindings) => Pdfium::new(bindings), // Create new bindings
574 Err(PdfiumError::PdfiumLibraryBindingsAlreadyInitialized) => Pdfium {
575 custom_font_provider: None,
576 platform_default_font_provider: None,
577 }, // Re-use the existing bindings
578 Err(PdfiumError::LoadLibraryError(err)) => {
579 match err {
580 libloading::Error::DlOpen { .. } => {
581 // For DlOpen errors specifically, indicating the Pdfium library in the
582 // current working directory does not exist or is corrupted, we attempt
583 // to fall back to a system-provided library.
584
585 Pdfium::new(
586 Pdfium::bind_to_system_library().expect("No Pdfium library found"),
587 )
588 }
589 _ => Err(PdfiumError::LoadLibraryError(err)).expect("No Pdfium library found"), // Explicitly re-throw the error
590 }
591 }
592 Err(err) => Err(err).expect("No Pdfium library found"), // Explicitly re-throw the error
593 }
594 }
595
596 #[cfg(target_arch = "wasm32")]
597 /// Binds to an external Pdfium library by attempting to a system-provided library.
598 ///
599 /// This function will panic if no suitable Pdfium library can be loaded.
600 fn default() -> Self {
601 Pdfium::new(Pdfium::bind_to_system_library().expect("No Pdfium library found"))
602 }
603}
604
605impl Debug for Pdfium {
606 #[inline]
607 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
608 f.debug_struct("Pdfium").finish()
609 }
610}
611
612#[cfg(not(target_arch = "wasm32"))]
613impl Drop for Pdfium {
614 fn drop(&mut self) {
615 if let Some(ptr) = self.platform_default_font_provider {
616 unsafe {
617 self.bindings().FPDF_FreeDefaultSystemFontInfo(ptr);
618 }
619 }
620 }
621}
622
623impl PdfiumLibraryBindingsAccessor<'_> for Pdfium {}
624
625#[cfg(feature = "thread_safe")]
626unsafe impl Sync for Pdfium {}
627
628#[cfg(feature = "thread_safe")]
629unsafe impl Send for Pdfium {}