skia_safe/modules/paragraph/
font_collection.rs

1use crate::{
2    interop::{self, FromStrs, VecSink},
3    prelude::*,
4    textlayout::ParagraphCache,
5    FontMgr, FontStyle, Typeface, Unichar,
6};
7use skia_bindings::{self as sb, skia_textlayout_FontCollection};
8use std::{ffi, fmt, ptr};
9
10use super::FontArguments;
11
12pub type FontCollection = RCHandle<skia_textlayout_FontCollection>;
13
14impl NativeRefCountedBase for skia_textlayout_FontCollection {
15    type Base = sb::SkRefCntBase;
16}
17
18impl fmt::Debug for FontCollection {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        f.debug_struct("FontCollection")
21            .field("font_managers_count", &self.font_managers_count())
22            .field("fallback_manager", &self.fallback_manager())
23            .field("font_fallback_enabled", &self.font_fallback_enabled())
24            .field("paragraph_cache", &self.paragraph_cache())
25            .finish()
26    }
27}
28
29impl FontCollection {
30    pub fn new() -> Self {
31        Self::from_ptr(unsafe { sb::C_FontCollection_new() }).unwrap()
32    }
33
34    pub fn font_managers_count(&self) -> usize {
35        unsafe { self.native().getFontManagersCount() }
36    }
37
38    pub fn set_asset_font_manager(&mut self, font_manager: impl Into<Option<FontMgr>>) {
39        unsafe {
40            sb::C_FontCollection_setAssetFontManager(
41                self.native_mut(),
42                font_manager.into().into_ptr_or_null(),
43            )
44        }
45    }
46
47    pub fn set_dynamic_font_manager(&mut self, font_manager: impl Into<Option<FontMgr>>) {
48        unsafe {
49            sb::C_FontCollection_setDynamicFontManager(
50                self.native_mut(),
51                font_manager.into().into_ptr_or_null(),
52            )
53        }
54    }
55
56    pub fn set_test_font_manager(&mut self, font_manager: impl Into<Option<FontMgr>>) {
57        unsafe {
58            sb::C_FontCollection_setTestFontManager(
59                self.native_mut(),
60                font_manager.into().into_ptr_or_null(),
61            )
62        }
63    }
64
65    pub fn set_default_font_manager<'a>(
66        &mut self,
67        font_manager: impl Into<Option<FontMgr>>,
68        default_family_name: impl Into<Option<&'a str>>,
69    ) {
70        let font_manager = font_manager.into();
71        unsafe {
72            match default_family_name.into() {
73                Some(name) => {
74                    let name = ffi::CString::new(name).unwrap();
75                    sb::C_FontCollection_setDefaultFontManager2(
76                        self.native_mut(),
77                        font_manager.into_ptr_or_null(),
78                        name.as_ptr(),
79                    )
80                }
81                None => sb::C_FontCollection_setDefaultFontManager(
82                    self.native_mut(),
83                    font_manager.into_ptr_or_null(),
84                ),
85            }
86        }
87    }
88
89    pub fn set_default_font_manager_and_family_names(
90        &mut self,
91        font_manager: impl Into<Option<FontMgr>>,
92        family_names: &[impl AsRef<str>],
93    ) {
94        let font_manager = font_manager.into();
95        let family_names = interop::Strings::from_strs(family_names);
96        unsafe {
97            sb::C_FontCollection_setDefaultFontManager3(
98                self.native_mut(),
99                font_manager.into_ptr_or_null(),
100                family_names.native(),
101            )
102        }
103    }
104
105    pub fn fallback_manager(&self) -> Option<FontMgr> {
106        FontMgr::from_ptr(unsafe { sb::C_FontCollection_getFallbackManager(self.native()) })
107    }
108
109    pub fn find_typefaces(
110        &mut self,
111        family_names: &[impl AsRef<str>],
112        font_style: FontStyle,
113    ) -> Vec<Typeface> {
114        self.find_typefaces_with_font_arguments(family_names, font_style, None)
115    }
116
117    pub fn find_typefaces_with_font_arguments<'fa>(
118        &mut self,
119        family_names: &[impl AsRef<str>],
120        font_style: FontStyle,
121        font_args: impl Into<Option<&'fa FontArguments>>,
122    ) -> Vec<Typeface> {
123        let family_names = interop::Strings::from_strs(family_names);
124
125        let mut typefaces: Vec<Typeface> = Vec::new();
126        let mut set_typefaces = |tfs: &mut [sb::sk_sp<sb::SkTypeface>]| {
127            typefaces = tfs
128                .iter_mut()
129                .filter_map(|sp| {
130                    let ptr = sp.fPtr;
131                    sp.fPtr = ptr::null_mut();
132                    Typeface::from_ptr(ptr)
133                })
134                .collect()
135        };
136
137        unsafe {
138            sb::C_FontCollection_findTypefaces(
139                self.native_mut(),
140                family_names.native(),
141                font_style.into_native(),
142                font_args.into().native_ptr_or_null(),
143                VecSink::new_mut(&mut set_typefaces).native_mut(),
144            )
145        };
146        typefaces
147    }
148
149    pub fn default_fallback_char<'fa>(
150        &mut self,
151        unicode: Unichar,
152        font_style: FontStyle,
153        locale: impl AsRef<str>,
154        font_args: impl Into<Option<&'fa FontArguments>>,
155    ) -> Option<Typeface> {
156        let locale = interop::String::from_str(locale.as_ref());
157        Typeface::from_ptr(unsafe {
158            sb::C_FontCollection_defaultFallback(
159                self.native_mut(),
160                unicode,
161                font_style.into_native(),
162                locale.native(),
163                font_args.into().native_ptr_or_null(),
164            )
165        })
166    }
167
168    pub fn default_fallback(&mut self) -> Option<Typeface> {
169        Typeface::from_ptr(unsafe { sb::C_FontCollection_defaultFallback2(self.native_mut()) })
170    }
171
172    pub fn default_emoji_fallback(
173        &mut self,
174        emoji_start: Unichar,
175        font_style: FontStyle,
176        locale: impl AsRef<str>,
177    ) -> Option<Typeface> {
178        let locale = interop::String::from_str(locale.as_ref());
179        Typeface::from_ptr(unsafe {
180            sb::C_FontCollection_defaultEmojiFallback(
181                self.native_mut(),
182                emoji_start,
183                font_style.into_native(),
184                locale.native(),
185            )
186        })
187    }
188
189    pub fn disable_font_fallback(&mut self) {
190        unsafe { self.native_mut().disableFontFallback() }
191    }
192
193    pub fn enable_font_fallback(&mut self) {
194        unsafe { self.native_mut().enableFontFallback() }
195    }
196
197    pub fn font_fallback_enabled(&self) -> bool {
198        unsafe { sb::C_FontCollection_fontFallbackEnabled(self.native()) }
199    }
200
201    pub fn paragraph_cache(&self) -> &ParagraphCache {
202        ParagraphCache::from_native_ref(unsafe {
203            &*sb::C_FontCollection_paragraphCache(self.native_mut_force())
204        })
205    }
206
207    pub fn paragraph_cache_mut(&mut self) -> &mut ParagraphCache {
208        ParagraphCache::from_native_ref_mut(unsafe {
209            &mut *sb::C_FontCollection_paragraphCache(self.native_mut())
210        })
211    }
212
213    pub fn clear_caches(&mut self) {
214        unsafe { self.native_mut().clearCaches() }
215    }
216}
217
218#[cfg(test)]
219mod tests {
220    use crate::prelude::*;
221    use crate::textlayout::FontCollection;
222    use crate::{FontMgr, FontStyle};
223
224    #[test]
225    #[serial_test::serial]
226    fn ref_counts() {
227        let mut fc = FontCollection::new();
228        assert_eq!(fc.native().ref_counted_base()._ref_cnt(), 1);
229
230        let fm = FontMgr::new();
231        let fm_base = fm.native().ref_counted_base()._ref_cnt();
232
233        fc.set_default_font_manager(fm.clone(), None);
234        assert_eq!(fm.native().ref_counted_base()._ref_cnt(), fm_base + 1);
235
236        let cloned_fc = fc.clone();
237        assert_eq!(fm.native().ref_counted_base()._ref_cnt(), fm_base + 1);
238        assert_eq!(fc.native().ref_counted_base()._ref_cnt(), 2);
239        drop(cloned_fc);
240        assert_eq!(fc.native().ref_counted_base()._ref_cnt(), 1);
241        assert_eq!(fm.native().ref_counted_base()._ref_cnt(), fm_base + 1);
242
243        {
244            let fmc = fc.fallback_manager().unwrap();
245            assert_eq!(fmc.native().ref_counted_base()._ref_cnt(), fm_base + 2);
246            drop(fmc);
247        }
248
249        fc.set_default_font_manager(None, None);
250        assert_eq!(fm.native().ref_counted_base()._ref_cnt(), fm_base);
251        drop(fm);
252        drop(fc);
253    }
254
255    #[test]
256    #[serial_test::serial]
257    fn find_typefaces() {
258        let mut fc = FontCollection::new();
259        fc.set_default_font_manager(FontMgr::new(), None);
260        println!("find typeface:");
261        for typeface in fc.find_typefaces(
262            &[
263                "Arial",
264                "Tahoma",
265                "Fira Code",
266                "JetBrains Mono",
267                "Not Existing",
268            ],
269            FontStyle::default(),
270        ) {
271            println!("typeface: {}", typeface.family_name());
272        }
273    }
274}