1use crate::bindgen::*;
11use std::collections::HashMap;
12use std::ffi::CStr;
13use std::os::raw::*;
14use std::ptr;
15use std::sync::Arc;
16
17#[derive(Clone, Debug)]
21pub struct FontDescriptor {
22 pub family: String,
24
25 pub weight: i32,
27
28 pub is_italic: bool,
30
31 pub charset: i32,
38
39 pub data: Arc<[u8]>,
42}
43
44#[derive(Hash, Eq, PartialEq, Clone, Debug)]
46struct FontKey {
47 family: String,
48 weight: i32,
49 is_italic: bool,
50 charset: i32,
51}
52
53struct FontHandle {
55 key: FontKey,
56}
57
58pub(crate) struct MemoryFontProvider {
63 sys_font_info: FPDF_SYSFONTINFO,
64 fonts: HashMap<FontKey, Arc<[u8]>>,
65}
66
67impl MemoryFontProvider {
68 pub(crate) fn new(descriptors: Vec<FontDescriptor>) -> Self {
70 let mut fonts = HashMap::new();
71 for descriptor in descriptors {
72 let key = FontKey {
73 family: descriptor.family.to_lowercase(),
74 weight: descriptor.weight,
75 is_italic: descriptor.is_italic,
76 charset: descriptor.charset,
77 };
78 fonts.insert(key, descriptor.data);
79 }
80
81 let sys_font_info = FPDF_SYSFONTINFO {
82 version: 2,
83 Release: Some(release_callback),
84 EnumFonts: None,
85 MapFont: Some(map_font_callback),
86 GetFont: Some(get_font_callback),
87 GetFontData: Some(get_font_data_callback),
88 GetFaceName: None,
89 GetFontCharset: None,
90 DeleteFont: Some(delete_font_callback),
91 };
92
93 MemoryFontProvider { sys_font_info, fonts }
94 }
95
96 pub(crate) fn as_mut_ptr(&mut self) -> *mut FPDF_SYSFONTINFO {
98 &mut self.sys_font_info as *mut FPDF_SYSFONTINFO
99 }
100
101 unsafe fn from_pthis<'a>(pthis: *mut FPDF_SYSFONTINFO) -> &'a mut Self {
108 unsafe { &mut *(pthis as *mut MemoryFontProvider) }
109 }
110}
111
112unsafe extern "C" fn release_callback(_pthis: *mut FPDF_SYSFONTINFO) {}
118
119unsafe extern "C" fn map_font_callback(
127 pthis: *mut FPDF_SYSFONTINFO,
128 weight: c_int,
129 bitalic: FPDF_BOOL,
130 charset: c_int,
131 _pitch_family: c_int,
132 face: *const c_char,
133 bexact: *mut FPDF_BOOL,
134) -> *mut c_void {
135 unsafe {
136 if pthis.is_null() || face.is_null() {
137 return ptr::null_mut();
138 }
139
140 let face_name = match CStr::from_ptr(face).to_str() {
141 Ok(s) => s,
142 Err(_) => return ptr::null_mut(),
143 };
144
145 let provider = MemoryFontProvider::from_pthis(pthis);
146 let face_lower = face_name.to_lowercase();
147 let is_italic = bitalic != 0;
148
149 let exact_key = FontKey {
150 family: face_lower.clone(),
151 weight,
152 is_italic,
153 charset,
154 };
155
156 if provider.fonts.contains_key(&exact_key) {
157 if !bexact.is_null() {
158 *bexact = 1;
159 }
160 let handle = Box::new(FontHandle { key: exact_key });
161 return Box::into_raw(handle) as *mut c_void;
162 }
163
164 let normal_weight_key = FontKey {
165 family: face_lower.clone(),
166 weight: 400,
167 is_italic,
168 charset,
169 };
170
171 if provider.fonts.contains_key(&normal_weight_key) {
172 if !bexact.is_null() {
173 *bexact = 0;
174 }
175 let handle = Box::new(FontHandle { key: normal_weight_key });
176 return Box::into_raw(handle) as *mut c_void;
177 }
178
179 for key in provider.fonts.keys() {
180 if key.family == face_lower && key.is_italic == is_italic {
181 if !bexact.is_null() {
182 *bexact = 0;
183 }
184 let handle = Box::new(FontHandle { key: key.clone() });
185 return Box::into_raw(handle) as *mut c_void;
186 }
187 }
188
189 ptr::null_mut()
190 }
191}
192
193unsafe extern "C" fn get_font_callback(pthis: *mut FPDF_SYSFONTINFO, face: *const c_char) -> *mut c_void {
197 unsafe {
198 if pthis.is_null() || face.is_null() {
199 return ptr::null_mut();
200 }
201
202 let face_name = match CStr::from_ptr(face).to_str() {
203 Ok(s) => s,
204 Err(_) => return ptr::null_mut(),
205 };
206
207 let provider = MemoryFontProvider::from_pthis(pthis);
208 let face_lower = face_name.to_lowercase();
209
210 for key in provider.fonts.keys() {
211 if key.family == face_lower {
212 let handle = Box::new(FontHandle { key: key.clone() });
213 return Box::into_raw(handle) as *mut c_void;
214 }
215 }
216
217 ptr::null_mut()
218 }
219}
220
221unsafe extern "C" fn get_font_data_callback(
226 pthis: *mut FPDF_SYSFONTINFO,
227 hfont: *mut c_void,
228 table: c_uint,
229 buffer: *mut c_uchar,
230 buf_size: c_ulong,
231) -> c_ulong {
232 unsafe {
233 if pthis.is_null() || hfont.is_null() {
234 return 0;
235 }
236
237 if table != 0 {
238 return 0;
239 }
240
241 let provider = MemoryFontProvider::from_pthis(pthis);
242 let handle = &*(hfont as *const FontHandle);
243
244 let font_data = match provider.fonts.get(&handle.key) {
245 Some(data) => data,
246 None => return 0,
247 };
248
249 let font_size = font_data.len() as c_ulong;
250
251 if buffer.is_null() {
252 return font_size;
253 }
254
255 let copy_size = std::cmp::min(buf_size, font_size) as usize;
256 if copy_size > 0 {
257 ptr::copy_nonoverlapping(font_data.as_ptr(), buffer, copy_size);
258 }
259
260 copy_size as c_ulong
261 }
262}
263
264unsafe extern "C" fn delete_font_callback(_pthis: *mut FPDF_SYSFONTINFO, hfont: *mut c_void) {
268 unsafe {
269 if hfont.is_null() {
270 return;
271 }
272
273 let _handle = Box::from_raw(hfont as *mut FontHandle);
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280
281 #[test]
282 fn test_font_key_case_insensitive() {
283 let key1 = FontKey {
284 family: "Arial".to_lowercase(),
285 weight: 400,
286 is_italic: false,
287 charset: 0,
288 };
289
290 let key2 = FontKey {
291 family: "arial".to_lowercase(),
292 weight: 400,
293 is_italic: false,
294 charset: 0,
295 };
296
297 assert_eq!(key1, key2);
298 }
299
300 #[test]
301 fn test_font_descriptor_creation() {
302 let data: Arc<[u8]> = Arc::from(vec![0u8, 1u8, 2u8].into_boxed_slice());
303 let descriptor = FontDescriptor {
304 family: "TestFont".to_string(),
305 weight: 400,
306 is_italic: false,
307 charset: 0,
308 data,
309 };
310
311 assert_eq!(descriptor.family, "TestFont");
312 assert_eq!(descriptor.weight, 400);
313 assert!(!descriptor.is_italic);
314 assert_eq!(descriptor.charset, 0);
315 }
316
317 #[test]
318 fn test_memory_font_provider_empty() {
319 let provider = MemoryFontProvider::new(vec![]);
320 assert_eq!(provider.fonts.len(), 0);
321 }
322
323 #[test]
324 fn test_memory_font_provider_with_fonts() {
325 let data: Arc<[u8]> = Arc::from(vec![0u8; 100].into_boxed_slice());
326 let descriptors = vec![
327 FontDescriptor {
328 family: "Arial".to_string(),
329 weight: 400,
330 is_italic: false,
331 charset: 0,
332 data: data.clone(),
333 },
334 FontDescriptor {
335 family: "Arial".to_string(),
336 weight: 700,
337 is_italic: false,
338 charset: 0,
339 data: data.clone(),
340 },
341 ];
342
343 let provider = MemoryFontProvider::new(descriptors);
344 assert_eq!(provider.fonts.len(), 2);
345 }
346
347 #[test]
348 fn test_memory_font_provider_sys_font_info() {
349 let provider = MemoryFontProvider::new(vec![]);
350 assert_eq!(provider.sys_font_info.version, 2);
351 assert!(provider.sys_font_info.Release.is_some());
352 assert!(provider.sys_font_info.MapFont.is_some());
353 assert!(provider.sys_font_info.GetFont.is_some());
354 assert!(provider.sys_font_info.GetFontData.is_some());
355 assert!(provider.sys_font_info.DeleteFont.is_some());
356 assert!(provider.sys_font_info.EnumFonts.is_none());
357 }
358}