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        self.default_fallback_char_and_families(
157            unicode,
158            &[] as &[&str],
159            font_style,
160            locale,
161            font_args,
162        )
163    }
164
165    pub fn default_fallback_char_and_families<'fa>(
166        &mut self,
167        unicode: Unichar,
168        family_names: &[impl AsRef<str>],
169        font_style: FontStyle,
170        locale: impl AsRef<str>,
171        font_args: impl Into<Option<&'fa FontArguments>>,
172    ) -> Option<Typeface> {
173        let family_names = interop::Strings::from_strs(family_names);
174        let locale = interop::String::from_str(locale.as_ref());
175        Typeface::from_ptr(unsafe {
176            sb::C_FontCollection_defaultFallback(
177                self.native_mut(),
178                unicode,
179                family_names.native(),
180                font_style.into_native(),
181                locale.native(),
182                font_args.into().native_ptr_or_null(),
183            )
184        })
185    }
186
187    pub fn default_fallback(&mut self) -> Option<Typeface> {
188        Typeface::from_ptr(unsafe { sb::C_FontCollection_defaultFallback2(self.native_mut()) })
189    }
190
191    pub fn default_emoji_fallback(
192        &mut self,
193        emoji_start: Unichar,
194        font_style: FontStyle,
195        locale: impl AsRef<str>,
196    ) -> Option<Typeface> {
197        let locale = interop::String::from_str(locale.as_ref());
198        Typeface::from_ptr(unsafe {
199            sb::C_FontCollection_defaultEmojiFallback(
200                self.native_mut(),
201                emoji_start,
202                font_style.into_native(),
203                locale.native(),
204            )
205        })
206    }
207
208    pub fn disable_font_fallback(&mut self) {
209        unsafe { self.native_mut().disableFontFallback() }
210    }
211
212    pub fn enable_font_fallback(&mut self) {
213        unsafe { self.native_mut().enableFontFallback() }
214    }
215
216    pub fn font_fallback_enabled(&self) -> bool {
217        unsafe { sb::C_FontCollection_fontFallbackEnabled(self.native()) }
218    }
219
220    pub fn paragraph_cache(&self) -> &ParagraphCache {
221        ParagraphCache::from_native_ref(unsafe {
222            &*sb::C_FontCollection_paragraphCache(self.native_mut_force())
223        })
224    }
225
226    pub fn paragraph_cache_mut(&mut self) -> &mut ParagraphCache {
227        ParagraphCache::from_native_ref_mut(unsafe {
228            &mut *sb::C_FontCollection_paragraphCache(self.native_mut())
229        })
230    }
231
232    pub fn clear_caches(&mut self) {
233        unsafe { self.native_mut().clearCaches() }
234    }
235}
236
237#[cfg(test)]
238mod tests {
239    use crate::prelude::*;
240    use crate::textlayout::FontCollection;
241    use crate::{FontMgr, FontStyle};
242
243    #[test]
244    #[serial_test::serial]
245    fn ref_counts() {
246        let mut fc = FontCollection::new();
247        assert_eq!(fc.native().ref_counted_base()._ref_cnt(), 1);
248
249        let fm = FontMgr::new();
250        let fm_base = fm.native().ref_counted_base()._ref_cnt();
251
252        fc.set_default_font_manager(fm.clone(), None);
253        assert_eq!(fm.native().ref_counted_base()._ref_cnt(), fm_base + 1);
254
255        let cloned_fc = fc.clone();
256        assert_eq!(fm.native().ref_counted_base()._ref_cnt(), fm_base + 1);
257        assert_eq!(fc.native().ref_counted_base()._ref_cnt(), 2);
258        drop(cloned_fc);
259        assert_eq!(fc.native().ref_counted_base()._ref_cnt(), 1);
260        assert_eq!(fm.native().ref_counted_base()._ref_cnt(), fm_base + 1);
261
262        {
263            let fmc = fc.fallback_manager().unwrap();
264            assert_eq!(fmc.native().ref_counted_base()._ref_cnt(), fm_base + 2);
265            drop(fmc);
266        }
267
268        fc.set_default_font_manager(None, None);
269        assert_eq!(fm.native().ref_counted_base()._ref_cnt(), fm_base);
270        drop(fm);
271        drop(fc);
272    }
273
274    #[test]
275    #[serial_test::serial]
276    fn find_typefaces() {
277        let mut fc = FontCollection::new();
278        fc.set_default_font_manager(FontMgr::new(), None);
279        println!("find typeface:");
280        for typeface in fc.find_typefaces(
281            &[
282                "Arial",
283                "Tahoma",
284                "Fira Code",
285                "JetBrains Mono",
286                "Not Existing",
287            ],
288            FontStyle::default(),
289        ) {
290            println!("typeface: {}", typeface.family_name());
291        }
292    }
293}