Skip to main content

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