Skip to main content

pdfium_render/
config.rs

1//! Defines the [PdfiumLibraryConfig] struct, used to pass custom initialization configuration
2//! to a new Pdfium library instance.
3
4use crate::bindgen::{
5    FPDF_LIBRARY_CONFIG, FPDF_RENDERER_TYPE_FPDF_RENDERERTYPE_AGG,
6    FPDF_RENDERER_TYPE_FPDF_RENDERERTYPE_SKIA,
7};
8use crate::error::PdfiumError;
9use std::ffi::{CString, NulError};
10use std::os::raw::{c_char, c_uint};
11use std::pin::Pin;
12use std::ptr::null_mut;
13use std::str::FromStr;
14
15#[cfg(not(target_arch = "wasm32"))]
16use std::os::raw::c_void;
17
18#[cfg(any(
19    feature = "pdfium_future",
20    feature = "pdfium_7881",
21    feature = "pdfium_7763",
22))]
23use crate::bindgen::{
24    FPDF_FONT_BACKEND_TYPE_FPDF_FONTBACKENDTYPE_FONTATIONS,
25    FPDF_FONT_BACKEND_TYPE_FPDF_FONTBACKENDTYPE_FREETYPE,
26};
27
28#[derive(Clone)]
29pub struct PdfiumLibraryConfig {
30    user_font_paths: Vec<CString>,
31    user_font_paths_ptrs: Pin<Box<Vec<*const c_char>>>,
32
33    #[cfg(not(target_arch = "wasm32"))]
34    v8_isolate_ptr: *mut c_void,
35    #[cfg(not(target_arch = "wasm32"))]
36    v8_embedder_slot_idx: c_uint,
37    #[cfg(not(target_arch = "wasm32"))]
38    v8_platform_ptr: *mut c_void,
39
40    renderer_type: c_uint,
41
42    #[cfg(any(
43        feature = "pdfium_future",
44        feature = "pdfium_7881",
45        feature = "pdfium_7763",
46    ))]
47    font_library_type: c_uint,
48}
49
50impl PdfiumLibraryConfig {
51    /// Creates a new [PdfiumLibraryConfig] object with all settings initialized to
52    /// their default values.
53    pub fn new() -> Self {
54        PdfiumLibraryConfig {
55            user_font_paths: vec![],
56            user_font_paths_ptrs: Box::pin(vec![]),
57
58            #[cfg(not(target_arch = "wasm32"))]
59            v8_isolate_ptr: null_mut(),
60            #[cfg(not(target_arch = "wasm32"))]
61            v8_embedder_slot_idx: 0,
62            #[cfg(not(target_arch = "wasm32"))]
63            v8_platform_ptr: null_mut(),
64
65            renderer_type: FPDF_RENDERER_TYPE_FPDF_RENDERERTYPE_SKIA,
66
67            #[cfg(any(
68                feature = "pdfium_future",
69                feature = "pdfium_7881",
70                feature = "pdfium_7763",
71            ))]
72            font_library_type: FPDF_FONT_BACKEND_TYPE_FPDF_FONTBACKENDTYPE_FREETYPE,
73        }
74    }
75
76    /// Clears any user-specified paths that should be interrogated by Pdfium when
77    /// attempting to load any custom fonts referenced in a PDF document.
78    #[inline]
79    pub fn clear_user_font_paths(self) -> Self {
80        self.set_user_font_paths(&[]).unwrap()
81    }
82
83    #[cfg(target_arch = "wasm32")]
84    /// Sets the list of user-specified paths that should be interrogated by Pdfium when
85    /// attempting to load any custom fonts referenced in a PDF document.
86    ///
87    /// Since the browser does not provide a font loading mechanism, this list of font paths
88    /// is empty when compiling to WASM.
89    #[inline]
90    pub fn set_platform_default_user_font_paths(self) -> Self {
91        self.clear_user_font_paths()
92    }
93
94    #[cfg(not(target_arch = "wasm32"))]
95    #[cfg(target_os = "linux")]
96    /// Sets the list of user-specified paths that should be interrogated by Pdfium when
97    /// attempting to load any custom fonts referenced in a PDF document.
98    ///
99    /// On Linux systems, the platform default font paths are `/usr/share/fonts/truetype/`
100    /// and `/usr/local/share/fonts/`.
101    #[inline]
102    pub fn set_platform_default_user_font_paths(self) -> Self {
103        self.set_user_font_paths(&["/usr/share/fonts/truetype/", "/usr/local/share/fonts/"])
104            .unwrap()
105    }
106
107    #[cfg(not(target_arch = "wasm32"))]
108    #[cfg(target_os = "macos")]
109    /// Sets the list of user-specified paths that should be interrogated by Pdfium when
110    /// attempting to load any custom fonts referenced in a PDF document.
111    ///
112    /// On macOS systems, the platform default font paths are `/Library/Fonts/` and
113    /// `/System/Library/Fonts/`.
114    #[inline]
115    pub fn set_platform_default_user_font_paths(self) -> Self {
116        self.set_user_font_paths(&["/Library/Fonts/", "/System/Library/Fonts/"])
117            .unwrap()
118    }
119
120    #[cfg(not(target_arch = "wasm32"))]
121    #[cfg(target_os = "windows")]
122    /// Sets the list of user-specified paths that should be interrogated by Pdfium when
123    /// attempting to load any custom fonts referenced in a PDF document.
124    ///
125    /// On Windows systems, the platform default font path is `C:\Windows\Fonts\`.
126    #[inline]
127    pub fn set_platform_default_user_font_paths(self) -> Self {
128        self.set_user_font_paths(&["C:\\Windows\\Fonts\\"]).unwrap()
129    }
130
131    /// Sets the list of user-specified paths that should be interrogated by Pdfium when
132    /// attempting to load any custom fonts referenced in a PDF document.
133    pub fn set_user_font_paths(mut self, paths: &[&str]) -> Result<Self, PdfiumError> {
134        let user_font_paths = paths
135            .iter()
136            .map(|path| CString::from_str(path))
137            .collect::<Result<Vec<CString>, NulError>>();
138
139        match user_font_paths {
140            Ok(paths) => {
141                let mut ptrs = paths.iter().map(|path| path.as_ptr()).collect::<Vec<_>>();
142
143                if !ptrs.is_empty() {
144                    ptrs.push(std::ptr::null());
145                }
146
147                self.user_font_paths = paths;
148                self.user_font_paths_ptrs = Box::pin(ptrs);
149
150                Ok(self)
151            }
152            Err(e) => Err(PdfiumError::InvalidUserFontPath(e)),
153        }
154    }
155
156    #[cfg(not(target_arch = "wasm32"))]
157    /// Sets the pointer to the `v8::Isolate` to use. If `NULL`, Pdfium will create one.
158    #[inline]
159    pub unsafe fn set_v8_isolate_ptr(mut self, ptr: *mut c_void) -> Self {
160        self.v8_isolate_ptr = ptr;
161        self
162    }
163
164    #[cfg(not(target_arch = "wasm32"))]
165    /// Sets the embedder data slot to use in the `v8::Isolate` to store Pdfium's per-isolate
166    /// data. The value needs to be in the range `[0, v8::Internals::kNumIsolateDataLots)`.
167    /// Note that `0` is fine for most embedders.
168    #[inline]
169    pub unsafe fn set_v8_embedder_slot(mut self, idx: c_uint) -> Self {
170        self.v8_embedder_slot_idx = idx;
171        self
172    }
173
174    #[cfg(not(target_arch = "wasm32"))]
175    /// Sets the pointer to the `v8::Platform` to use.
176    #[inline]
177    pub unsafe fn set_v8_platform_ptr(mut self, ptr: &mut c_void) -> Self {
178        self.v8_platform_ptr = ptr;
179        self
180    }
181
182    /// Sets Pdfium's graphics renderer to the Anti-Grain Geometry library, <https://sourceforge.net/projects/agg/>.
183    #[inline]
184    pub fn set_renderer_anti_grain_geometry(mut self) -> Self {
185        self.renderer_type = FPDF_RENDERER_TYPE_FPDF_RENDERERTYPE_AGG;
186        self
187    }
188
189    /// Sets Pdfium's graphics renderer to Skia, <https://skia.org/>.
190    #[inline]
191    pub fn set_renderer_skia(mut self) -> Self {
192        self.renderer_type = FPDF_RENDERER_TYPE_FPDF_RENDERERTYPE_SKIA;
193        self
194    }
195
196    #[cfg(any(
197        feature = "pdfium_future",
198        feature = "pdfium_7881",
199        feature = "pdfium_7763",
200    ))]
201    /// Sets Pdfium's font handler to FreeType, <https://freetype.org/>.
202    #[inline]
203    pub fn set_font_backend_freetype(mut self) -> Self {
204        self.font_library_type = FPDF_FONT_BACKEND_TYPE_FPDF_FONTBACKENDTYPE_FREETYPE;
205        self
206    }
207
208    #[cfg(any(
209        feature = "pdfium_future",
210        feature = "pdfium_7881",
211        feature = "pdfium_7763",
212    ))]
213    /// Sets Pdfium's font handler to Fontations, <https://github.com/googlefonts/fontations/>.
214    #[inline]
215    pub fn set_font_backend_fontations(mut self) -> Self {
216        self.font_library_type = FPDF_FONT_BACKEND_TYPE_FPDF_FONTBACKENDTYPE_FONTATIONS;
217        self
218    }
219
220    /// Returns a `FPDF_LIBRARY_CONFIG` instance from this [PdfiumLibraryConfig] instance
221    /// that can be passed to Pdfium's `FPDF_FPDF_InitLibraryWithConfig()` function.
222    pub(crate) fn as_pdfium(&mut self) -> FPDF_LIBRARY_CONFIG {
223        let config = FPDF_LIBRARY_CONFIG {
224            version: 2,
225            m_pUserFontPaths: if self.user_font_paths_ptrs.is_empty() {
226                std::ptr::null_mut()
227            } else {
228                self.user_font_paths_ptrs.as_mut_ptr()
229            },
230
231            #[cfg(not(target_arch = "wasm32"))]
232            m_pIsolate: self.v8_isolate_ptr,
233            #[cfg(not(target_arch = "wasm32"))]
234            m_v8EmbedderSlot: self.v8_embedder_slot_idx,
235            #[cfg(not(target_arch = "wasm32"))]
236            m_pPlatform: self.v8_platform_ptr,
237
238            #[cfg(target_arch = "wasm32")]
239            m_pIsolate: null_mut(),
240            #[cfg(target_arch = "wasm32")]
241            m_v8EmbedderSlot: 0,
242            #[cfg(target_arch = "wasm32")]
243            m_pPlatform: null_mut(),
244
245            m_RendererType: self.renderer_type,
246
247            #[cfg(any(
248                feature = "pdfium_future",
249                feature = "pdfium_7881",
250                feature = "pdfium_7763",
251            ))]
252            m_FontLibraryType: self.font_library_type,
253        };
254
255        config
256    }
257}
258
259#[cfg(feature = "thread_safe")]
260unsafe impl Sync for PdfiumLibraryConfig {}
261
262#[cfg(feature = "thread_safe")]
263unsafe impl Send for PdfiumLibraryConfig {}
264
265#[cfg(test)]
266mod tests {
267    use super::PdfiumLibraryConfig;
268    use std::ffi::CStr;
269
270    #[test]
271    fn user_font_paths_are_kept_alive_and_null_terminated() {
272        let mut config = PdfiumLibraryConfig::new()
273            .set_user_font_paths(&["/first/font/path", "/second/font/path"])
274            .unwrap();
275
276        assert_eq!(
277            config.as_pdfium().m_pUserFontPaths,
278            config.user_font_paths_ptrs.as_ptr().cast_mut()
279        );
280        assert_eq!(config.user_font_paths.len(), 2); // The number of paths submitted by the user
281        assert_eq!(config.user_font_paths_ptrs.len(), 3); // Always one extra for the trailing null entry
282        assert_eq!(
283            unsafe { CStr::from_ptr(config.user_font_paths_ptrs[0]) }
284                .to_str()
285                .unwrap(),
286            "/first/font/path"
287        );
288        assert_eq!(
289            unsafe { CStr::from_ptr(config.user_font_paths_ptrs[1]) }
290                .to_str()
291                .unwrap(),
292            "/second/font/path"
293        );
294        assert!(config.user_font_paths_ptrs[2].is_null());
295    }
296
297    #[test]
298    fn empty_user_font_paths_use_a_null_pointer() {
299        let mut config = PdfiumLibraryConfig::new();
300
301        assert!(config.as_pdfium().m_pUserFontPaths.is_null());
302        assert!(config.user_font_paths_ptrs.is_empty());
303    }
304}