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&mut DebugStruct<'_, '_>.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(name:"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}
273