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    // Custom empty manager. This avoids scanning system fonts when they are not required.
117    //
118    // Returns `None` on platforms where Skia is not compiled with freetype (e.g. Windows)
119    pub fn custom_empty() -> Option<Self> {
120        FontMgr::from_ptr(unsafe { sb::C_SkFontMgr_NewCustomEmpty() })
121    }
122
123    pub fn count_families(&self) -> usize {
124        unsafe { self.native().countFamilies().try_into().unwrap() }
125    }
126
127    pub fn family_name(&self, index: usize) -> String {
128        assert!(index < self.count_families());
129        let mut family_name = interop::String::default();
130        unsafe {
131            self.native()
132                .getFamilyName(index.try_into().unwrap(), family_name.native_mut())
133        }
134        family_name.as_str().into()
135    }
136
137    pub fn family_names(&self) -> impl Iterator<Item = String> + use<'_> {
138        (0..self.count_families()).map(move |i| self.family_name(i))
139    }
140
141    #[deprecated(since = "0.41.0", note = "Use new_style_set")]
142    pub fn new_styleset(&self, index: usize) -> FontStyleSet {
143        self.new_style_set(index)
144    }
145
146    pub fn new_style_set(&self, index: usize) -> FontStyleSet {
147        assert!(index < self.count_families());
148        FontStyleSet::from_ptr(unsafe {
149            sb::C_SkFontMgr_createStyleSet(self.native(), index.try_into().unwrap())
150        })
151        .unwrap()
152    }
153
154    pub fn match_family(&self, family_name: impl AsRef<str>) -> FontStyleSet {
155        let family_name = CString::new(family_name.as_ref()).unwrap();
156        FontStyleSet::from_ptr(unsafe {
157            sb::C_SkFontMgr_matchFamily(self.native(), family_name.as_ptr())
158        })
159        .unwrap()
160    }
161
162    pub fn match_family_style(
163        &self,
164        family_name: impl AsRef<str>,
165        style: FontStyle,
166    ) -> Option<Typeface> {
167        let family_name = CString::new(family_name.as_ref()).unwrap();
168        Typeface::from_ptr(unsafe {
169            sb::C_SkFontMgr_matchFamilyStyle(self.native(), family_name.as_ptr(), style.native())
170        })
171    }
172
173    // TODO: support IntoIterator / AsRef<str> for bcp_47?
174    pub fn match_family_style_character(
175        &self,
176        family_name: impl AsRef<str>,
177        style: FontStyle,
178        bcp_47: &[&str],
179        character: Unichar,
180    ) -> Option<Typeface> {
181        let family_name = CString::new(family_name.as_ref()).unwrap();
182        // create backing store for the pointer array.
183        let bcp_47: Vec<CString> = bcp_47.iter().map(|s| CString::new(*s).unwrap()).collect();
184        // note: mutability needed to comply to the C type "const char* bcp47[]".
185        let mut bcp_47: Vec<*const c_char> = bcp_47.iter().map(|cs| cs.as_ptr()).collect();
186
187        Typeface::from_ptr(unsafe {
188            sb::C_SkFontMgr_matchFamilyStyleCharacter(
189                self.native(),
190                family_name.as_ptr(),
191                style.native(),
192                bcp_47.as_mut_ptr(),
193                bcp_47.len().try_into().unwrap(),
194                character,
195            )
196        })
197    }
198
199    #[deprecated(since = "0.35.0", note = "Removed without replacement")]
200    pub fn match_face_style(&self, _typeface: impl AsRef<Typeface>, _style: FontStyle) -> ! {
201        panic!("Removed without replacement")
202    }
203
204    // pub fn new_from_data(
205    //     &self,
206    //     bytes: &[u8],
207    //     ttc_index: impl Into<Option<usize>>,
208    // ) -> Option<Typeface> {
209    //     let data: Data = Data::new_copy(bytes);
210    //     Typeface::from_ptr(unsafe {
211    //         sb::C_SkFontMgr_makeFromData(
212    //             self.native(),
213    //             data.into_ptr(),
214    //             ttc_index.into().unwrap_or_default().try_into().unwrap(),
215    //         )
216    //     })
217    // }
218
219    pub fn new_from_data(
220        &self,
221        bytes: &[u8],
222        ttc_index: impl Into<Option<usize>>,
223    ) -> Option<Typeface> {
224        let mut stream = DynamicMemoryWStream::from_bytes(bytes);
225        let mut stream = stream.detach_as_stream();
226        Typeface::from_ptr(unsafe {
227            let stream_ptr = stream.native_mut() as *mut _;
228            // makeFromStream takes ownership of the stream, so don't drop it.
229            mem::forget(stream);
230            sb::C_SkFontMgr_makeFromStream(
231                self.native(),
232                stream_ptr,
233                ttc_index.into().unwrap_or_default().try_into().unwrap(),
234            )
235        })
236    }
237
238    pub fn legacy_make_typeface<'a>(
239        &self,
240        family_name: impl Into<Option<&'a str>>,
241        style: FontStyle,
242    ) -> Option<Typeface> {
243        let family_name: Option<CString> = family_name
244            .into()
245            .and_then(|family_name| CString::new(family_name).ok());
246
247        Typeface::from_ptr(unsafe {
248            sb::C_SkFontMgr_legacyMakeTypeface(
249                self.native(),
250                family_name
251                    .as_ref()
252                    .map(|n| n.as_ptr())
253                    .unwrap_or(ptr::null()),
254                style.into_native(),
255            )
256        })
257    }
258
259    // TODO: makeFromStream(.., ttcIndex).
260}
261
262#[cfg(test)]
263mod tests {
264    use crate::FontMgr;
265
266    #[test]
267    #[serial_test::serial]
268    fn create_all_typefaces() {
269        let font_mgr = FontMgr::default();
270        let families = font_mgr.count_families();
271        println!("FontMgr families: {families}");
272        // This test requires that the default system font manager returns at least one family for now.
273        assert!(families > 0);
274        // print all family names and styles
275        for i in 0..families {
276            let name = font_mgr.family_name(i);
277            println!("font_family: {name}");
278            let mut style_set = font_mgr.new_style_set(i);
279            for style_index in 0..style_set.count() {
280                let (_, style_name) = style_set.style(style_index);
281                if let Some(style_name) = style_name {
282                    println!("  style: {style_name}");
283                }
284                let face = style_set.new_typeface(style_index);
285                drop(face);
286            }
287        }
288    }
289}