1 | use skia_bindings::{self as sb, SkFontMgr, SkFontStyleSet, SkRefCntBase}; |
2 | use std::{ffi::CString, fmt, mem, os::raw::c_char, ptr}; |
3 | |
4 | use crate::{ |
5 | interop::{self, DynamicMemoryWStream}, |
6 | prelude::*, |
7 | FontStyle, Typeface, Unichar, |
8 | }; |
9 | |
10 | pub type FontStyleSet = RCHandle<SkFontStyleSet>; |
11 | |
12 | impl NativeBase<SkRefCntBase> for SkFontStyleSet {} |
13 | |
14 | impl NativeRefCountedBase for SkFontStyleSet { |
15 | type Base = SkRefCntBase; |
16 | } |
17 | |
18 | impl Default for FontStyleSet { |
19 | fn default() -> Self { |
20 | FontStyleSet::new_empty() |
21 | } |
22 | } |
23 | |
24 | impl fmt::Debug for FontStyleSet { |
25 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
26 | fDebugStruct<'_, '_>.debug_struct(name:"FontStyleSet" ) |
27 | // TODO: clarify why self has to be mut. |
28 | // .field("count", &self.count()) |
29 | .finish() |
30 | } |
31 | } |
32 | |
33 | impl FontStyleSet { |
34 | pub fn count(&mut self) -> usize { |
35 | unsafe { |
36 | sb::C_SkFontStyleSet_count(self.native_mut()) |
37 | .try_into() |
38 | .unwrap() |
39 | } |
40 | } |
41 | |
42 | pub fn style(&mut self, index: usize) -> (FontStyle, Option<String>) { |
43 | assert!(index < self.count()); |
44 | |
45 | let mut font_style = FontStyle::default(); |
46 | let mut style = interop::String::default(); |
47 | unsafe { |
48 | sb::C_SkFontStyleSet_getStyle( |
49 | self.native_mut(), |
50 | index.try_into().unwrap(), |
51 | font_style.native_mut(), |
52 | style.native_mut(), |
53 | ) |
54 | } |
55 | |
56 | // Note: Android's FontMgr returns empty style names. |
57 | let name = style |
58 | .as_str() |
59 | .is_empty() |
60 | .if_false_then_some(|| style.as_str().into()); |
61 | |
62 | (font_style, name) |
63 | } |
64 | |
65 | pub fn new_typeface(&mut self, index: usize) -> Option<Typeface> { |
66 | assert!(index < self.count()); |
67 | |
68 | Typeface::from_ptr(unsafe { |
69 | sb::C_SkFontStyleSet_createTypeface(self.native_mut(), index.try_into().unwrap()) |
70 | }) |
71 | } |
72 | |
73 | pub fn match_style(&mut self, pattern: FontStyle) -> Option<Typeface> { |
74 | Typeface::from_ptr(unsafe { |
75 | sb::C_SkFontStyleSet_matchStyle(self.native_mut(), pattern.native()) |
76 | }) |
77 | } |
78 | |
79 | pub fn new_empty() -> Self { |
80 | FontStyleSet::from_ptr(unsafe { sb::C_SkFontStyleSet_CreateEmpty() }).unwrap() |
81 | } |
82 | } |
83 | |
84 | pub type FontMgr = RCHandle<SkFontMgr>; |
85 | |
86 | impl NativeBase<SkRefCntBase> for SkFontMgr {} |
87 | |
88 | impl NativeRefCountedBase for SkFontMgr { |
89 | type Base = SkRefCntBase; |
90 | } |
91 | |
92 | impl Default for FontMgr { |
93 | fn default() -> Self { |
94 | Self::new() |
95 | } |
96 | } |
97 | |
98 | impl fmt::Debug for FontMgr { |
99 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
100 | let names: Vec<_> = self.family_names().collect(); |
101 | f&mut DebugStruct<'_, '_>.debug_struct("FontMgr" ) |
102 | .field(name:"family_names" , &names) |
103 | .finish() |
104 | } |
105 | } |
106 | |
107 | impl FontMgr { |
108 | // Deprecated by Skia, but we continue to support it. This returns a font manager with |
109 | // system fonts for the current platform. |
110 | pub fn new() -> Self { |
111 | FontMgr::from_ptr(unsafe { sb::C_SkFontMgr_NewSystem() }).unwrap() |
112 | } |
113 | |
114 | pub fn empty() -> Self { |
115 | FontMgr::from_ptr(unsafe { sb::C_SkFontMgr_RefEmpty() }).unwrap() |
116 | } |
117 | |
118 | pub fn count_families(&self) -> usize { |
119 | unsafe { self.native().countFamilies().try_into().unwrap() } |
120 | } |
121 | |
122 | pub fn family_name(&self, index: usize) -> String { |
123 | assert!(index < self.count_families()); |
124 | let mut family_name = interop::String::default(); |
125 | unsafe { |
126 | self.native() |
127 | .getFamilyName(index.try_into().unwrap(), family_name.native_mut()) |
128 | } |
129 | family_name.as_str().into() |
130 | } |
131 | |
132 | pub fn family_names(&self) -> impl Iterator<Item = String> + use<'_> { |
133 | (0..self.count_families()).map(move |i| self.family_name(i)) |
134 | } |
135 | |
136 | #[deprecated (since = "0.41.0" , note = "Use new_style_set" )] |
137 | pub fn new_styleset(&self, index: usize) -> FontStyleSet { |
138 | self.new_style_set(index) |
139 | } |
140 | |
141 | pub fn new_style_set(&self, index: usize) -> FontStyleSet { |
142 | assert!(index < self.count_families()); |
143 | FontStyleSet::from_ptr(unsafe { |
144 | sb::C_SkFontMgr_createStyleSet(self.native(), index.try_into().unwrap()) |
145 | }) |
146 | .unwrap() |
147 | } |
148 | |
149 | pub fn match_family(&self, family_name: impl AsRef<str>) -> FontStyleSet { |
150 | let family_name = CString::new(family_name.as_ref()).unwrap(); |
151 | FontStyleSet::from_ptr(unsafe { |
152 | sb::C_SkFontMgr_matchFamily(self.native(), family_name.as_ptr()) |
153 | }) |
154 | .unwrap() |
155 | } |
156 | |
157 | pub fn match_family_style( |
158 | &self, |
159 | family_name: impl AsRef<str>, |
160 | style: FontStyle, |
161 | ) -> Option<Typeface> { |
162 | let family_name = CString::new(family_name.as_ref()).unwrap(); |
163 | Typeface::from_ptr(unsafe { |
164 | sb::C_SkFontMgr_matchFamilyStyle(self.native(), family_name.as_ptr(), style.native()) |
165 | }) |
166 | } |
167 | |
168 | // TODO: support IntoIterator / AsRef<str> for bcp_47? |
169 | pub fn match_family_style_character( |
170 | &self, |
171 | family_name: impl AsRef<str>, |
172 | style: FontStyle, |
173 | bcp_47: &[&str], |
174 | character: Unichar, |
175 | ) -> Option<Typeface> { |
176 | let family_name = CString::new(family_name.as_ref()).unwrap(); |
177 | // create backing store for the pointer array. |
178 | let bcp_47: Vec<CString> = bcp_47.iter().map(|s| CString::new(*s).unwrap()).collect(); |
179 | // note: mutability needed to comply to the C type "const char* bcp47[]". |
180 | let mut bcp_47: Vec<*const c_char> = bcp_47.iter().map(|cs| cs.as_ptr()).collect(); |
181 | |
182 | Typeface::from_ptr(unsafe { |
183 | sb::C_SkFontMgr_matchFamilyStyleCharacter( |
184 | self.native(), |
185 | family_name.as_ptr(), |
186 | style.native(), |
187 | bcp_47.as_mut_ptr(), |
188 | bcp_47.len().try_into().unwrap(), |
189 | character, |
190 | ) |
191 | }) |
192 | } |
193 | |
194 | #[deprecated (since = "0.35.0" , note = "Removed without replacement" )] |
195 | pub fn match_face_style(&self, _typeface: impl AsRef<Typeface>, _style: FontStyle) -> ! { |
196 | panic!("Removed without replacement" ) |
197 | } |
198 | |
199 | // pub fn new_from_data( |
200 | // &self, |
201 | // bytes: &[u8], |
202 | // ttc_index: impl Into<Option<usize>>, |
203 | // ) -> Option<Typeface> { |
204 | // let data: Data = Data::new_copy(bytes); |
205 | // Typeface::from_ptr(unsafe { |
206 | // sb::C_SkFontMgr_makeFromData( |
207 | // self.native(), |
208 | // data.into_ptr(), |
209 | // ttc_index.into().unwrap_or_default().try_into().unwrap(), |
210 | // ) |
211 | // }) |
212 | // } |
213 | |
214 | pub fn new_from_data( |
215 | &self, |
216 | bytes: &[u8], |
217 | ttc_index: impl Into<Option<usize>>, |
218 | ) -> Option<Typeface> { |
219 | let mut stream = DynamicMemoryWStream::from_bytes(bytes); |
220 | let mut stream = stream.detach_as_stream(); |
221 | Typeface::from_ptr(unsafe { |
222 | let stream_ptr = stream.native_mut() as *mut _; |
223 | // makeFromStream takes ownership of the stream, so don't drop it. |
224 | mem::forget(stream); |
225 | sb::C_SkFontMgr_makeFromStream( |
226 | self.native(), |
227 | stream_ptr, |
228 | ttc_index.into().unwrap_or_default().try_into().unwrap(), |
229 | ) |
230 | }) |
231 | } |
232 | |
233 | pub fn legacy_make_typeface<'a>( |
234 | &self, |
235 | family_name: impl Into<Option<&'a str>>, |
236 | style: FontStyle, |
237 | ) -> Option<Typeface> { |
238 | let family_name: Option<CString> = family_name |
239 | .into() |
240 | .and_then(|family_name| CString::new(family_name).ok()); |
241 | |
242 | Typeface::from_ptr(unsafe { |
243 | sb::C_SkFontMgr_legacyMakeTypeface( |
244 | self.native(), |
245 | family_name |
246 | .as_ref() |
247 | .map(|n| n.as_ptr()) |
248 | .unwrap_or(ptr::null()), |
249 | style.into_native(), |
250 | ) |
251 | }) |
252 | } |
253 | |
254 | // TODO: makeFromStream(.., ttcIndex). |
255 | } |
256 | |
257 | #[cfg (test)] |
258 | mod tests { |
259 | use crate::FontMgr; |
260 | |
261 | #[test ] |
262 | #[serial_test::serial] |
263 | fn create_all_typefaces() { |
264 | let font_mgr = FontMgr::default(); |
265 | let families = font_mgr.count_families(); |
266 | println!("FontMgr families: {families}" ); |
267 | // This test requires that the default system font manager returns at least one family for now. |
268 | assert!(families > 0); |
269 | // print all family names and styles |
270 | for i in 0..families { |
271 | let name = font_mgr.family_name(i); |
272 | println!("font_family: {name}" ); |
273 | let mut style_set = font_mgr.new_style_set(i); |
274 | for style_index in 0..style_set.count() { |
275 | let (_, style_name) = style_set.style(style_index); |
276 | if let Some(style_name) = style_name { |
277 | println!(" style: {style_name}" ); |
278 | } |
279 | let face = style_set.new_typeface(style_index); |
280 | drop(face); |
281 | } |
282 | } |
283 | } |
284 | } |
285 | |