1use 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 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 #[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 #[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 #[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 #[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 #[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 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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); assert_eq!(config.user_font_paths_ptrs.len(), 3); 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}