Skip to main content

firecrawl_pdfium/sys/
bindings.rs

1//! The PDFium function table, resolved from a dynamically loaded library.
2//!
3//! Every function bound here was transcribed from the headers shipped in the
4//! `pdfium-binaries` archives (see `types.rs` for the same statement about
5//! structs). Symbols are resolved eagerly in [`Bindings::load_from_library`]:
6//! a library missing any bound symbol is rejected at load time with
7//! [`MissingSymbolError`], never at call time.
8
9#![allow(non_snake_case)] // fields/methods intentionally mirror the C API
10
11use std::ffi::{c_int, c_uchar, c_uint, c_ulong, c_ushort, c_void};
12use std::fmt;
13
14use super::types::*;
15
16/// Error produced when a symbol required by the bindings table is absent
17/// from the loaded library — in practice, a PDFium build older than the
18/// oldest version this crate supports.
19#[derive(Debug)]
20pub struct MissingSymbolError {
21    pub symbol: &'static str,
22    pub source: libloading::Error,
23}
24
25impl fmt::Display for MissingSymbolError {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        write!(
28            f,
29            "PDFium library is missing required symbol `{}` (library too old or not PDFium)",
30            self.symbol
31        )
32    }
33}
34
35impl std::error::Error for MissingSymbolError {
36    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
37        Some(&self.source)
38    }
39}
40
41macro_rules! pdfium_bindings {
42    ($( fn $name:ident($($arg:ident: $ty:ty),* $(,)?) $(-> $ret:ty)?; )*) => {
43        /// Table of resolved PDFium function pointers plus the library they
44        /// were resolved from.
45        ///
46        /// # Safety contract
47        ///
48        /// Every method on this type is `unsafe` and shares two
49        /// preconditions beyond the per-function pointer/handle validity
50        /// requirements documented by PDFium:
51        ///
52        /// 1. **Serialization.** PDFium is not thread-safe. The caller must
53        ///    guarantee that no two PDFium calls (through any `Bindings`
54        ///    instance in the process) execute concurrently. The safe layer
55        ///    does this with a process-global mutex.
56        /// 2. **Initialization.** `FPDF_InitLibraryWithConfig` must have been
57        ///    called (once) before any other function, and
58        ///    `FPDF_DestroyLibrary` must not have been called.
59        pub struct Bindings {
60            $( $name: unsafe extern "C" fn($($ty),*) $(-> $ret)?, )*
61            /// Keeps the shared library mapped. Field order is irrelevant
62            /// for safety here because `Bindings` is never dropped while a
63            /// function pointer is in flight (the safe layer holds it in a
64            /// process-lifetime static), but the library is declared last so
65            /// pointers never outlive it even in a hypothetical drop.
66            _library: libloading::Library,
67        }
68
69        impl Bindings {
70            /// Resolves the complete function table from `library`.
71            ///
72            /// # Safety
73            ///
74            /// The library must be PDFium (or ABI-compatible): each resolved
75            /// symbol is blindly trusted to have the C signature transcribed
76            /// in this file. Loading symbols from an unrelated library that
77            /// happens to export these names is undefined behavior.
78            pub unsafe fn load_from_library(
79                library: libloading::Library,
80            ) -> Result<Box<Self>, MissingSymbolError> {
81                Ok(Box::new(Bindings {
82                    $(
83                        // SAFETY: symbol type is the signature transcribed
84                        // from the PDFium headers; caller guarantees the
85                        // library is PDFium (this function's contract).
86                        $name: *unsafe {
87                            library.get(concat!(stringify!($name), "\0").as_bytes())
88                        }
89                        .map_err(|source| MissingSymbolError {
90                            symbol: stringify!($name),
91                            source,
92                        })?,
93                    )*
94                    _library: library,
95                }))
96            }
97
98            $(
99                /// Direct call to the PDFium function of the same name.
100                ///
101                /// # Safety
102                ///
103                /// See the type-level safety contract, plus the PDFium
104                /// documentation for this function's own preconditions.
105                #[allow(non_snake_case, clippy::too_many_arguments)]
106                #[inline]
107                pub unsafe fn $name(&self, $($arg: $ty),*) $(-> $ret)? {
108                    // SAFETY: forwarded verbatim; the caller upholds this
109                    // method's documented preconditions.
110                    unsafe { (self.$name)($($arg),*) }
111                }
112            )*
113        }
114    };
115}
116
117pdfium_bindings! {
118    // --- Library lifecycle (fpdfview.h) ---
119    fn FPDF_InitLibraryWithConfig(config: *const FPDF_LIBRARY_CONFIG);
120    fn FPDF_DestroyLibrary();
121    fn FPDF_GetLastError() -> c_ulong;
122
123    // --- Document (fpdfview.h) ---
124    fn FPDF_LoadMemDocument64(
125        data_buf: *const c_void,
126        size: usize,
127        password: FPDF_BYTESTRING,
128    ) -> FPDF_DOCUMENT;
129    fn FPDF_CloseDocument(document: FPDF_DOCUMENT);
130    fn FPDF_GetPageCount(document: FPDF_DOCUMENT) -> c_int;
131    fn FPDF_GetDocPermissions(document: FPDF_DOCUMENT) -> c_ulong;
132    fn FPDF_GetSecurityHandlerRevision(document: FPDF_DOCUMENT) -> c_int;
133    fn FPDF_GetFileVersion(doc: FPDF_DOCUMENT, fileVersion: *mut c_int) -> FPDF_BOOL;
134    fn FPDF_GetPageSizeByIndexF(
135        document: FPDF_DOCUMENT,
136        page_index: c_int,
137        size: *mut FS_SIZEF,
138    ) -> FPDF_BOOL;
139
140    // --- Page (fpdfview.h / fpdf_edit.h) ---
141    fn FPDF_LoadPage(document: FPDF_DOCUMENT, page_index: c_int) -> FPDF_PAGE;
142    fn FPDF_ClosePage(page: FPDF_PAGE);
143    fn FPDF_GetPageWidthF(page: FPDF_PAGE) -> f32;
144    fn FPDF_GetPageHeightF(page: FPDF_PAGE) -> f32;
145    fn FPDF_GetPageBoundingBox(page: FPDF_PAGE, rect: *mut FS_RECTF) -> FPDF_BOOL;
146    fn FPDFPage_GetRotation(page: FPDF_PAGE) -> c_int;
147    fn FPDFPage_HasTransparency(page: FPDF_PAGE) -> FPDF_BOOL;
148
149    // --- Rendering (fpdfview.h) ---
150    fn FPDF_RenderPageBitmap(
151        bitmap: FPDF_BITMAP,
152        page: FPDF_PAGE,
153        start_x: c_int,
154        start_y: c_int,
155        size_x: c_int,
156        size_y: c_int,
157        rotate: c_int,
158        flags: c_int,
159    );
160    fn FPDF_RenderPageBitmapWithMatrix(
161        bitmap: FPDF_BITMAP,
162        page: FPDF_PAGE,
163        matrix: *const FS_MATRIX,
164        clipping: *const FS_RECTF,
165        flags: c_int,
166    );
167
168    // --- Coordinate transforms (fpdfview.h) ---
169    fn FPDF_DeviceToPage(
170        page: FPDF_PAGE,
171        start_x: c_int,
172        start_y: c_int,
173        size_x: c_int,
174        size_y: c_int,
175        rotate: c_int,
176        device_x: c_int,
177        device_y: c_int,
178        page_x: *mut f64,
179        page_y: *mut f64,
180    ) -> FPDF_BOOL;
181    fn FPDF_PageToDevice(
182        page: FPDF_PAGE,
183        start_x: c_int,
184        start_y: c_int,
185        size_x: c_int,
186        size_y: c_int,
187        rotate: c_int,
188        page_x: f64,
189        page_y: f64,
190        device_x: *mut c_int,
191        device_y: *mut c_int,
192    ) -> FPDF_BOOL;
193
194    // --- Bitmaps (fpdfview.h) ---
195    fn FPDFBitmap_CreateEx(
196        width: c_int,
197        height: c_int,
198        format: c_int,
199        first_scan: *mut c_void,
200        stride: c_int,
201    ) -> FPDF_BITMAP;
202    fn FPDFBitmap_FillRect(
203        bitmap: FPDF_BITMAP,
204        left: c_int,
205        top: c_int,
206        width: c_int,
207        height: c_int,
208        color: FPDF_DWORD,
209    ) -> FPDF_BOOL;
210    fn FPDFBitmap_Destroy(bitmap: FPDF_BITMAP);
211    fn FPDFBitmap_GetBuffer(bitmap: FPDF_BITMAP) -> *mut c_void;
212    fn FPDFBitmap_GetWidth(bitmap: FPDF_BITMAP) -> c_int;
213    fn FPDFBitmap_GetHeight(bitmap: FPDF_BITMAP) -> c_int;
214    fn FPDFBitmap_GetStride(bitmap: FPDF_BITMAP) -> c_int;
215    fn FPDFBitmap_GetFormat(bitmap: FPDF_BITMAP) -> c_int;
216
217    // --- Metadata (fpdf_doc.h) ---
218    fn FPDF_GetMetaText(
219        document: FPDF_DOCUMENT,
220        tag: FPDF_BYTESTRING,
221        buffer: *mut c_void,
222        buflen: c_ulong,
223    ) -> c_ulong;
224    fn FPDF_GetPageLabel(
225        document: FPDF_DOCUMENT,
226        page_index: c_int,
227        buffer: *mut c_void,
228        buflen: c_ulong,
229    ) -> c_ulong;
230
231    // --- Text extraction (fpdf_text.h) ---
232    fn FPDFText_LoadPage(page: FPDF_PAGE) -> FPDF_TEXTPAGE;
233    fn FPDFText_ClosePage(text_page: FPDF_TEXTPAGE);
234    fn FPDFText_CountChars(text_page: FPDF_TEXTPAGE) -> c_int;
235    fn FPDFText_GetText(
236        text_page: FPDF_TEXTPAGE,
237        start_index: c_int,
238        count: c_int,
239        result: *mut c_ushort,
240    ) -> c_int;
241    fn FPDFText_GetUnicode(text_page: FPDF_TEXTPAGE, index: c_int) -> c_uint;
242    fn FPDFText_GetCharBox(
243        text_page: FPDF_TEXTPAGE,
244        index: c_int,
245        left: *mut f64,
246        right: *mut f64,
247        bottom: *mut f64,
248        top: *mut f64,
249    ) -> FPDF_BOOL;
250    fn FPDFText_GetCharOrigin(
251        text_page: FPDF_TEXTPAGE,
252        index: c_int,
253        x: *mut f64,
254        y: *mut f64,
255    ) -> FPDF_BOOL;
256    fn FPDFText_GetLooseCharBox(
257        text_page: FPDF_TEXTPAGE,
258        index: c_int,
259        rect: *mut FS_RECTF,
260    ) -> FPDF_BOOL;
261    fn FPDFText_CountRects(text_page: FPDF_TEXTPAGE, start_index: c_int, count: c_int) -> c_int;
262    fn FPDFText_GetRect(
263        text_page: FPDF_TEXTPAGE,
264        rect_index: c_int,
265        left: *mut f64,
266        top: *mut f64,
267        right: *mut f64,
268        bottom: *mut f64,
269    ) -> FPDF_BOOL;
270
271    // --- Forms (fpdf_formfill.h) ---
272    fn FPDFDOC_InitFormFillEnvironment(
273        document: FPDF_DOCUMENT,
274        formInfo: *mut FPDF_FORMFILLINFO,
275    ) -> FPDF_FORMHANDLE;
276    fn FPDFDOC_ExitFormFillEnvironment(hHandle: FPDF_FORMHANDLE);
277    fn FPDF_FFLDraw(
278        hHandle: FPDF_FORMHANDLE,
279        bitmap: FPDF_BITMAP,
280        page: FPDF_PAGE,
281        start_x: c_int,
282        start_y: c_int,
283        size_x: c_int,
284        size_y: c_int,
285        rotate: c_int,
286        flags: c_int,
287    );
288    fn FORM_OnAfterLoadPage(page: FPDF_PAGE, hHandle: FPDF_FORMHANDLE);
289    fn FORM_OnBeforeClosePage(page: FPDF_PAGE, hHandle: FPDF_FORMHANDLE);
290    fn FPDF_GetFormType(document: FPDF_DOCUMENT) -> c_int;
291    fn FPDF_SetFormFieldHighlightColor(
292        hHandle: FPDF_FORMHANDLE,
293        fieldType: c_int,
294        color: c_ulong,
295    );
296    fn FPDF_SetFormFieldHighlightAlpha(hHandle: FPDF_FORMHANDLE, alpha: c_uchar);
297}
298
299// `FPDF_InitLibrary` (config-less) is intentionally not bound: we always use
300// `FPDF_InitLibraryWithConfig` with a version-2 config and null font paths.
301// `FPDF_LoadMemDocument` (32-bit size) is superseded by the 64-bit variant.
302// Windows-only symbols (`FPDF_RenderPage`, `FPDF_SetPrintMode`) and
303// Skia-only symbols must never be added to this table: it is resolved
304// eagerly on every platform.