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