1 | use crate::{ |
2 | interop::{self, FromStrs, VecSink}, |
3 | prelude::*, |
4 | textlayout::ParagraphCache, |
5 | FontMgr, FontStyle, Typeface, Unichar, |
6 | }; |
7 | use skia_bindings::{self as sb, skia_textlayout_FontCollection}; |
8 | use std::{ffi, fmt, ptr}; |
9 | |
10 | use super::FontArguments; |
11 | |
12 | pub type FontCollection = RCHandle<skia_textlayout_FontCollection>; |
13 | |
14 | impl NativeRefCountedBase for skia_textlayout_FontCollection { |
15 | type Base = sb::SkRefCntBase; |
16 | } |
17 | |
18 | impl 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 | |
29 | impl 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)] |
217 | mod 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 | |