Skip to main content

skia_safe/core/
font_mgr.rs

1use skia_bindings::{self as sb, SkFontMgr, SkFontStyleSet, SkRefCntBase};
2use std::{ffi::CString, fmt, mem, os::raw::c_char, ptr};
3
4use crate::{
5    FontStyle, Typeface, Unichar, font_arguments,
6    interop::{self, DynamicMemoryWStream},
7    prelude::*,
8};
9
10pub mod request {
11    use skia_bindings::{self as sb, SkFontMgr_Request_CMapEntry};
12
13    use crate::{FontStyle, Unichar, font_arguments, prelude::*};
14
15    #[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
16    #[repr(C)]
17    pub struct CMapEntry {
18        pub character: Unichar,
19        pub variation: Unichar,
20    }
21
22    native_transmutable!(SkFontMgr_Request_CMapEntry, CMapEntry);
23
24    pub fn font_style_from_model(
25        model: &[font_arguments::variation_position::Coordinate],
26    ) -> FontStyle {
27        FontStyle::construct(|font_style| unsafe {
28            sb::C_SkFontMgr_Request_fontStyleFromModel(
29                model.native().as_ptr(),
30                model.len(),
31                font_style,
32            )
33        })
34    }
35
36    pub fn model_from_font_style(
37        font_style: FontStyle,
38    ) -> [font_arguments::variation_position::Coordinate; 4] {
39        let mut model = [font_arguments::variation_position::Coordinate::default(); 4];
40        unsafe {
41            sb::C_SkFontMgr_Request_SetModel(font_style.native(), model.native_mut().as_mut_ptr())
42        }
43        model
44    }
45}
46
47#[derive(Clone, Debug, Default)]
48pub struct Request<'a> {
49    pub cmap_entries: &'a [request::CMapEntry],
50    pub bcp_47: &'a [&'a str],
51    pub family_name: Option<&'a str>,
52    pub model: &'a [font_arguments::variation_position::Coordinate],
53    pub synthetic_bold: Option<bool>,
54    pub synthetic_oblique: Option<bool>,
55}
56
57pub type FontStyleSet = RCHandle<SkFontStyleSet>;
58
59impl NativeBase<SkRefCntBase> for SkFontStyleSet {}
60
61impl NativeRefCountedBase for SkFontStyleSet {
62    type Base = SkRefCntBase;
63}
64
65impl Default for FontStyleSet {
66    fn default() -> Self {
67        FontStyleSet::new_empty()
68    }
69}
70
71impl fmt::Debug for FontStyleSet {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        f.debug_struct("FontStyleSet").finish()
74    }
75}
76
77impl FontStyleSet {
78    pub fn count(&mut self) -> usize {
79        unsafe {
80            sb::C_SkFontStyleSet_count(self.native_mut())
81                .try_into()
82                .unwrap()
83        }
84    }
85
86    pub fn style(&mut self, index: usize) -> (FontStyle, Option<String>) {
87        assert!(index < self.count());
88
89        let mut font_style = FontStyle::default();
90        let mut style = interop::String::default();
91        unsafe {
92            sb::C_SkFontStyleSet_getStyle(
93                self.native_mut(),
94                index.try_into().unwrap(),
95                font_style.native_mut(),
96                style.native_mut(),
97            )
98        }
99
100        let style = style.as_str();
101        // Note: Android's FontMgr returns empty style names.
102        let name = (!style.is_empty()).then(|| style.into());
103
104        (font_style, name)
105    }
106
107    pub fn new_typeface(&mut self, index: usize) -> Option<Typeface> {
108        assert!(index < self.count());
109
110        Typeface::from_ptr(unsafe {
111            sb::C_SkFontStyleSet_createTypeface(self.native_mut(), index.try_into().unwrap())
112        })
113    }
114
115    pub fn match_style(&mut self, pattern: FontStyle) -> Option<Typeface> {
116        Typeface::from_ptr(unsafe {
117            sb::C_SkFontStyleSet_matchStyle(self.native_mut(), pattern.native())
118        })
119    }
120
121    pub fn new_empty() -> Self {
122        FontStyleSet::from_ptr(unsafe { sb::C_SkFontStyleSet_CreateEmpty() }).unwrap()
123    }
124}
125
126pub type FontMgr = RCHandle<SkFontMgr>;
127
128impl NativeBase<SkRefCntBase> for SkFontMgr {}
129
130impl NativeRefCountedBase for SkFontMgr {
131    type Base = SkRefCntBase;
132}
133
134impl Default for FontMgr {
135    fn default() -> Self {
136        Self::new()
137    }
138}
139
140impl fmt::Debug for FontMgr {
141    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        let names: Vec<_> = self.family_names().collect();
143        f.debug_struct("FontMgr")
144            .field("family_names", &names)
145            .finish()
146    }
147}
148
149impl FontMgr {
150    // Deprecated by Skia, but we continue to support it. This returns a font manager with
151    // system fonts for the current platform.
152    pub fn new() -> Self {
153        FontMgr::from_ptr(unsafe { sb::C_SkFontMgr_NewSystem() }).unwrap()
154    }
155
156    pub fn empty() -> Self {
157        FontMgr::from_ptr(unsafe { sb::C_SkFontMgr_RefEmpty() }).unwrap()
158    }
159
160    // Custom empty manager. This avoids scanning system fonts when they are not required.
161    //
162    // Returns `None` on platforms where Skia is not compiled with freetype (e.g. Windows)
163    pub fn custom_empty() -> Option<Self> {
164        FontMgr::from_ptr(unsafe { sb::C_SkFontMgr_NewCustomEmpty() })
165    }
166
167    pub fn count_families(&self) -> usize {
168        unsafe { self.native().countFamilies().try_into().unwrap() }
169    }
170
171    pub fn family_name(&self, index: usize) -> String {
172        assert!(index < self.count_families());
173        let mut family_name = interop::String::default();
174        unsafe {
175            self.native()
176                .getFamilyName(index.try_into().unwrap(), family_name.native_mut())
177        }
178        family_name.as_str().into()
179    }
180
181    pub fn family_names(&self) -> impl Iterator<Item = String> + use<'_> {
182        (0..self.count_families()).map(move |i| self.family_name(i))
183    }
184
185    #[deprecated(since = "0.41.0", note = "Use new_style_set")]
186    pub fn new_styleset(&self, index: usize) -> FontStyleSet {
187        self.new_style_set(index)
188    }
189
190    pub fn new_style_set(&self, index: usize) -> FontStyleSet {
191        assert!(index < self.count_families());
192        FontStyleSet::from_ptr(unsafe {
193            sb::C_SkFontMgr_createStyleSet(self.native(), index.try_into().unwrap())
194        })
195        .unwrap()
196    }
197
198    pub fn match_family(&self, family_name: impl AsRef<str>) -> FontStyleSet {
199        let family_name = CString::new(family_name.as_ref()).unwrap();
200        FontStyleSet::from_ptr(unsafe {
201            sb::C_SkFontMgr_matchFamily(self.native(), family_name.as_ptr())
202        })
203        .unwrap()
204    }
205
206    pub fn match_family_style(
207        &self,
208        family_name: impl AsRef<str>,
209        style: FontStyle,
210    ) -> Option<Typeface> {
211        let family_name = CString::new(family_name.as_ref()).unwrap();
212        Typeface::from_ptr(unsafe {
213            sb::C_SkFontMgr_matchFamilyStyle(self.native(), family_name.as_ptr(), style.native())
214        })
215    }
216
217    // TODO: support IntoIterator / AsRef<str> for bcp_47?
218    pub fn match_family_style_character(
219        &self,
220        family_name: impl AsRef<str>,
221        style: FontStyle,
222        bcp_47: &[&str],
223        character: Unichar,
224    ) -> Option<Typeface> {
225        let family_name = CString::new(family_name.as_ref()).unwrap();
226        // Create backing store for the pointer array.
227        let bcp_47: Vec<CString> = bcp_47.iter().map(|s| CString::new(*s).unwrap()).collect();
228        // Note: mutability needed to comply to the C type "const char* bcp47[]".
229        let mut bcp_47: Vec<*const c_char> = bcp_47.iter().map(|cs| cs.as_ptr()).collect();
230
231        Typeface::from_ptr(unsafe {
232            sb::C_SkFontMgr_matchFamilyStyleCharacter(
233                self.native(),
234                family_name.as_ptr(),
235                style.native(),
236                bcp_47.as_mut_ptr(),
237                bcp_47.len().try_into().unwrap(),
238                character,
239            )
240        })
241    }
242
243    pub fn match_request(&self, request: &Request<'_>) -> Option<Typeface> {
244        with_ffi_request(request, |ffi_request| {
245            Typeface::from_ptr(unsafe { sb::C_SkFontMgr_match(self.native(), ffi_request) })
246        })
247    }
248
249    pub fn fallback(&self, request: &Request<'_>) -> Option<Typeface> {
250        with_ffi_request(request, |ffi_request| {
251            Typeface::from_ptr(unsafe { sb::C_SkFontMgr_fallback(self.native(), ffi_request) })
252        })
253    }
254
255    pub fn fallback_request(&self, request: &Request<'_>) -> Option<Typeface> {
256        self.fallback(request)
257    }
258
259    #[deprecated(since = "0.35.0", note = "Removed without replacement")]
260    pub fn match_face_style(&self, _typeface: impl AsRef<Typeface>, _style: FontStyle) -> ! {
261        panic!("Removed without replacement")
262    }
263
264    // pub fn new_from_data(
265    //     &self,
266    //     bytes: &[u8],
267    //     ttc_index: impl Into<Option<usize>>,
268    // ) -> Option<Typeface> {
269    //     let data: Data = Data::new_copy(bytes);
270    //     Typeface::from_ptr(unsafe {
271    //         sb::C_SkFontMgr_makeFromData(
272    //             self.native(),
273    //             data.into_ptr(),
274    //             ttc_index.into().unwrap_or_default().try_into().unwrap(),
275    //         )
276    //     })
277    // }
278
279    pub fn new_from_data(
280        &self,
281        bytes: &[u8],
282        ttc_index: impl Into<Option<usize>>,
283    ) -> Option<Typeface> {
284        let mut stream = DynamicMemoryWStream::from_bytes(bytes);
285        let mut stream = stream.detach_as_stream();
286        Typeface::from_ptr(unsafe {
287            let stream_ptr = stream.native_mut() as *mut _;
288            // makeFromStream takes ownership of the stream, so don't drop it.
289            mem::forget(stream);
290            sb::C_SkFontMgr_makeFromStream(
291                self.native(),
292                stream_ptr,
293                ttc_index.into().unwrap_or_default().try_into().unwrap(),
294            )
295        })
296    }
297
298    pub fn legacy_make_typeface<'a>(
299        &self,
300        family_name: impl Into<Option<&'a str>>,
301        style: FontStyle,
302    ) -> Option<Typeface> {
303        let family_name: Option<CString> = family_name
304            .into()
305            .and_then(|family_name| CString::new(family_name).ok());
306
307        Typeface::from_ptr(unsafe {
308            sb::C_SkFontMgr_legacyMakeTypeface(
309                self.native(),
310                family_name
311                    .as_ref()
312                    .map(|n| n.as_ptr())
313                    .unwrap_or(ptr::null()),
314                style.into_native(),
315            )
316        })
317    }
318
319    // TODO: makeFromStream(.., ttcIndex).
320}
321
322fn with_ffi_request<T>(request: &Request<'_>, f: impl FnOnce(&sb::C_SkFontMgr_Request) -> T) -> T {
323    let family_name = request
324        .family_name
325        .and_then(|family_name| CString::new(family_name).ok());
326    let bcp_47: Vec<CString> = request
327        .bcp_47
328        .iter()
329        .map(|s| CString::new(*s).unwrap())
330        .collect();
331    let mut bcp_47_ptrs: Vec<*const c_char> = bcp_47.iter().map(|cs| cs.as_ptr()).collect();
332    let bcp_47_ptr = if bcp_47_ptrs.is_empty() {
333        ptr::null_mut()
334    } else {
335        bcp_47_ptrs.as_mut_ptr()
336    };
337
338    let ffi_request = sb::C_SkFontMgr_Request {
339        cmapEntries: request.cmap_entries.native().as_ptr(),
340        cmapEntryCount: request.cmap_entries.len(),
341        bcp47: bcp_47_ptr,
342        bcp47Count: bcp_47_ptrs.len(),
343        familyName: family_name
344            .as_ref()
345            .map(|n| n.as_ptr())
346            .unwrap_or(ptr::null()),
347        model: request.model.native().as_ptr(),
348        modelCount: request.model.len(),
349        syntheticBold: option_bool_to_ffi(request.synthetic_bold),
350        syntheticOblique: option_bool_to_ffi(request.synthetic_oblique),
351    };
352
353    f(&ffi_request)
354}
355
356fn option_bool_to_ffi(value: Option<bool>) -> i32 {
357    match value {
358        Some(true) => 1,
359        Some(false) => 0,
360        None => -1,
361    }
362}
363
364#[cfg(test)]
365mod tests {
366    use crate::{
367        FontMgr, FontStyle,
368        font_mgr::{Request, request},
369    };
370
371    #[test]
372    #[serial_test::serial]
373    fn create_all_typefaces() {
374        let font_mgr = FontMgr::default();
375        let families = font_mgr.count_families();
376        println!("FontMgr families: {families}");
377        // This test requires that the default system font manager returns at least one family for now.
378        assert!(families > 0);
379        // Print all family names and styles
380        for i in 0..families {
381            let name = font_mgr.family_name(i);
382            println!("font_family: {name}");
383            let mut style_set = font_mgr.new_style_set(i);
384            for style_index in 0..style_set.count() {
385                let (_, style_name) = style_set.style(style_index);
386                if let Some(style_name) = style_name {
387                    println!("  style: {style_name}");
388                }
389                let face = style_set.new_typeface(style_index);
390                drop(face);
391            }
392        }
393    }
394
395    #[test]
396    fn request_apis_accept_default_request() {
397        let font_mgr = FontMgr::empty();
398        let request = Request::default();
399
400        let _ = font_mgr.match_request(&request);
401        let _ = font_mgr.fallback(&request);
402
403        let _ = request::font_style_from_model(&[]);
404        let _ = request::model_from_font_style(FontStyle::default());
405    }
406}