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