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