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    interop::{self, DynamicMemoryWStream},
6    prelude::*,
7    FontStyle, Typeface, Unichar,
8};
9
10pub type FontStyleSet = RCHandle<SkFontStyleSet>;
11
12impl NativeBase<SkRefCntBase> for SkFontStyleSet {}
13
14impl NativeRefCountedBase for SkFontStyleSet {
15    type Base = SkRefCntBase;
16}
17
18impl Default for FontStyleSet {
19    fn default() -> Self {
20        FontStyleSet::new_empty()
21    }
22}
23
24impl fmt::Debug for FontStyleSet {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        f.debug_struct("FontStyleSet")
27            // TODO: clarify why self has to be mut.
28            // .field("count", &self.count())
29            .finish()
30    }
31}
32
33impl FontStyleSet {
34    pub fn count(&mut self) -> usize {
35        unsafe {
36            sb::C_SkFontStyleSet_count(self.native_mut())
37                .try_into()
38                .unwrap()
39        }
40    }
41
42    pub fn style(&mut self, index: usize) -> (FontStyle, Option<String>) {
43        assert!(index < self.count());
44
45        let mut font_style = FontStyle::default();
46        let mut style = interop::String::default();
47        unsafe {
48            sb::C_SkFontStyleSet_getStyle(
49                self.native_mut(),
50                index.try_into().unwrap(),
51                font_style.native_mut(),
52                style.native_mut(),
53            )
54        }
55
56        let style = style.as_str();
57        // Note: Android's FontMgr returns empty style names.
58        let name = (!style.is_empty()).then(|| style.into());
59
60        (font_style, name)
61    }
62
63    pub fn new_typeface(&mut self, index: usize) -> Option<Typeface> {
64        assert!(index < self.count());
65
66        Typeface::from_ptr(unsafe {
67            sb::C_SkFontStyleSet_createTypeface(self.native_mut(), index.try_into().unwrap())
68        })
69    }
70
71    pub fn match_style(&mut self, pattern: FontStyle) -> Option<Typeface> {
72        Typeface::from_ptr(unsafe {
73            sb::C_SkFontStyleSet_matchStyle(self.native_mut(), pattern.native())
74        })
75    }
76
77    pub fn new_empty() -> Self {
78        FontStyleSet::from_ptr(unsafe { sb::C_SkFontStyleSet_CreateEmpty() }).unwrap()
79    }
80}
81
82pub type FontMgr = RCHandle<SkFontMgr>;
83
84impl NativeBase<SkRefCntBase> for SkFontMgr {}
85
86impl NativeRefCountedBase for SkFontMgr {
87    type Base = SkRefCntBase;
88}
89
90impl Default for FontMgr {
91    fn default() -> Self {
92        Self::new()
93    }
94}
95
96impl fmt::Debug for FontMgr {
97    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98        let names: Vec<_> = self.family_names().collect();
99        f.debug_struct("FontMgr")
100            .field("family_names", &names)
101            .finish()
102    }
103}
104
105impl FontMgr {
106    // Deprecated by Skia, but we continue to support it. This returns a font manager with
107    // system fonts for the current platform.
108    pub fn new() -> Self {
109        FontMgr::from_ptr(unsafe { sb::C_SkFontMgr_NewSystem() }).unwrap()
110    }
111
112    pub fn empty() -> Self {
113        FontMgr::from_ptr(unsafe { sb::C_SkFontMgr_RefEmpty() }).unwrap()
114    }
115
116    pub fn count_families(&self) -> usize {
117        unsafe { self.native().countFamilies().try_into().unwrap() }
118    }
119
120    pub fn family_name(&self, index: usize) -> String {
121        assert!(index < self.count_families());
122        let mut family_name = interop::String::default();
123        unsafe {
124            self.native()
125                .getFamilyName(index.try_into().unwrap(), family_name.native_mut())
126        }
127        family_name.as_str().into()
128    }
129
130    pub fn family_names(&self) -> impl Iterator<Item = String> + use<'_> {
131        (0..self.count_families()).map(move |i| self.family_name(i))
132    }
133
134    #[deprecated(since = "0.41.0", note = "Use new_style_set")]
135    pub fn new_styleset(&self, index: usize) -> FontStyleSet {
136        self.new_style_set(index)
137    }
138
139    pub fn new_style_set(&self, index: usize) -> FontStyleSet {
140        assert!(index < self.count_families());
141        FontStyleSet::from_ptr(unsafe {
142            sb::C_SkFontMgr_createStyleSet(self.native(), index.try_into().unwrap())
143        })
144        .unwrap()
145    }
146
147    pub fn match_family(&self, family_name: impl AsRef<str>) -> FontStyleSet {
148        let family_name = CString::new(family_name.as_ref()).unwrap();
149        FontStyleSet::from_ptr(unsafe {
150            sb::C_SkFontMgr_matchFamily(self.native(), family_name.as_ptr())
151        })
152        .unwrap()
153    }
154
155    pub fn match_family_style(
156        &self,
157        family_name: impl AsRef<str>,
158        style: FontStyle,
159    ) -> Option<Typeface> {
160        let family_name = CString::new(family_name.as_ref()).unwrap();
161        Typeface::from_ptr(unsafe {
162            sb::C_SkFontMgr_matchFamilyStyle(self.native(), family_name.as_ptr(), style.native())
163        })
164    }
165
166    // TODO: support IntoIterator / AsRef<str> for bcp_47?
167    pub fn match_family_style_character(
168        &self,
169        family_name: impl AsRef<str>,
170        style: FontStyle,
171        bcp_47: &[&str],
172        character: Unichar,
173    ) -> Option<Typeface> {
174        let family_name = CString::new(family_name.as_ref()).unwrap();
175        // create backing store for the pointer array.
176        let bcp_47: Vec<CString> = bcp_47.iter().map(|s| CString::new(*s).unwrap()).collect();
177        // note: mutability needed to comply to the C type "const char* bcp47[]".
178        let mut bcp_47: Vec<*const c_char> = bcp_47.iter().map(|cs| cs.as_ptr()).collect();
179
180        Typeface::from_ptr(unsafe {
181            sb::C_SkFontMgr_matchFamilyStyleCharacter(
182                self.native(),
183                family_name.as_ptr(),
184                style.native(),
185                bcp_47.as_mut_ptr(),
186                bcp_47.len().try_into().unwrap(),
187                character,
188            )
189        })
190    }
191
192    #[deprecated(since = "0.35.0", note = "Removed without replacement")]
193    pub fn match_face_style(&self, _typeface: impl AsRef<Typeface>, _style: FontStyle) -> ! {
194        panic!("Removed without replacement")
195    }
196
197    // pub fn new_from_data(
198    //     &self,
199    //     bytes: &[u8],
200    //     ttc_index: impl Into<Option<usize>>,
201    // ) -> Option<Typeface> {
202    //     let data: Data = Data::new_copy(bytes);
203    //     Typeface::from_ptr(unsafe {
204    //         sb::C_SkFontMgr_makeFromData(
205    //             self.native(),
206    //             data.into_ptr(),
207    //             ttc_index.into().unwrap_or_default().try_into().unwrap(),
208    //         )
209    //     })
210    // }
211
212    pub fn new_from_data(
213        &self,
214        bytes: &[u8],
215        ttc_index: impl Into<Option<usize>>,
216    ) -> Option<Typeface> {
217        let mut stream = DynamicMemoryWStream::from_bytes(bytes);
218        let mut stream = stream.detach_as_stream();
219        Typeface::from_ptr(unsafe {
220            let stream_ptr = stream.native_mut() as *mut _;
221            // makeFromStream takes ownership of the stream, so don't drop it.
222            mem::forget(stream);
223            sb::C_SkFontMgr_makeFromStream(
224                self.native(),
225                stream_ptr,
226                ttc_index.into().unwrap_or_default().try_into().unwrap(),
227            )
228        })
229    }
230
231    pub fn legacy_make_typeface<'a>(
232        &self,
233        family_name: impl Into<Option<&'a str>>,
234        style: FontStyle,
235    ) -> Option<Typeface> {
236        let family_name: Option<CString> = family_name
237            .into()
238            .and_then(|family_name| CString::new(family_name).ok());
239
240        Typeface::from_ptr(unsafe {
241            sb::C_SkFontMgr_legacyMakeTypeface(
242                self.native(),
243                family_name
244                    .as_ref()
245                    .map(|n| n.as_ptr())
246                    .unwrap_or(ptr::null()),
247                style.into_native(),
248            )
249        })
250    }
251
252    // TODO: makeFromStream(.., ttcIndex).
253}
254
255#[cfg(test)]
256mod tests {
257    use crate::FontMgr;
258
259    #[test]
260    #[serial_test::serial]
261    fn create_all_typefaces() {
262        let font_mgr = FontMgr::default();
263        let families = font_mgr.count_families();
264        println!("FontMgr families: {families}");
265        // This test requires that the default system font manager returns at least one family for now.
266        assert!(families > 0);
267        // print all family names and styles
268        for i in 0..families {
269            let name = font_mgr.family_name(i);
270            println!("font_family: {name}");
271            let mut style_set = font_mgr.new_style_set(i);
272            for style_index in 0..style_set.count() {
273                let (_, style_name) = style_set.style(style_index);
274                if let Some(style_name) = style_name {
275                    println!("  style: {style_name}");
276                }
277                let face = style_set.new_typeface(style_index);
278                drop(face);
279            }
280        }
281    }
282}