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