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, index: usize, pattern: FontStyle) -> Option<Typeface> { |
74 | assert!(index < self.count()); |
75 | Typeface::from_ptr(unsafe { |
76 | sb::C_SkFontStyleSet_matchStyle(self.native_mut(), pattern.native()) |
77 | }) |
78 | } |
79 | |
80 | pub fn new_empty() -> Self { |
81 | FontStyleSet::from_ptr(unsafe { sb::C_SkFontStyleSet_CreateEmpty() }).unwrap() |
82 | } |
83 | } |
84 | |
85 | pub type FontMgr = RCHandle<SkFontMgr>; |
86 | |
87 | impl NativeBase<SkRefCntBase> for SkFontMgr {} |
88 | |
89 | impl NativeRefCountedBase for SkFontMgr { |
90 | type Base = SkRefCntBase; |
91 | } |
92 | |
93 | impl Default for FontMgr { |
94 | fn default() -> Self { |
95 | Self::new() |
96 | } |
97 | } |
98 | |
99 | impl fmt::Debug for FontMgr { |
100 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
101 | let names: Vec<_> = self.family_names().collect(); |
102 | f&mut DebugStruct<'_, '_>.debug_struct("FontMgr" ) |
103 | .field(name:"family_names" , &names) |
104 | .finish() |
105 | } |
106 | } |
107 | |
108 | impl FontMgr { |
109 | // Deprecated by Skia, but we continue to support it. This returns a font manager with |
110 | // system fonts for the current platform. |
111 | pub fn new() -> Self { |
112 | FontMgr::from_ptr(unsafe { sb::C_SkFontMgr_NewSystem() }).unwrap() |
113 | } |
114 | |
115 | pub fn empty() -> Self { |
116 | FontMgr::from_ptr(unsafe { sb::C_SkFontMgr_RefEmpty() }).unwrap() |
117 | } |
118 | |
119 | pub fn count_families(&self) -> usize { |
120 | unsafe { self.native().countFamilies().try_into().unwrap() } |
121 | } |
122 | |
123 | pub fn family_name(&self, index: usize) -> String { |
124 | assert!(index < self.count_families()); |
125 | let mut family_name = interop::String::default(); |
126 | unsafe { |
127 | self.native() |
128 | .getFamilyName(index.try_into().unwrap(), family_name.native_mut()) |
129 | } |
130 | family_name.as_str().into() |
131 | } |
132 | |
133 | pub fn family_names(&self) -> impl Iterator<Item = String> + Captures<&Self> { |
134 | (0..self.count_families()).map(move |i| self.family_name(i)) |
135 | } |
136 | |
137 | #[deprecated (since = "0.41.0" , note = "Use new_style_set" )] |
138 | pub fn new_styleset(&self, index: usize) -> FontStyleSet { |
139 | self.new_style_set(index) |
140 | } |
141 | |
142 | pub fn new_style_set(&self, index: usize) -> FontStyleSet { |
143 | assert!(index < self.count_families()); |
144 | FontStyleSet::from_ptr(unsafe { |
145 | sb::C_SkFontMgr_createStyleSet(self.native(), index.try_into().unwrap()) |
146 | }) |
147 | .unwrap() |
148 | } |
149 | |
150 | pub fn match_family(&self, family_name: impl AsRef<str>) -> FontStyleSet { |
151 | let family_name = CString::new(family_name.as_ref()).unwrap(); |
152 | FontStyleSet::from_ptr(unsafe { |
153 | sb::C_SkFontMgr_matchFamily(self.native(), family_name.as_ptr()) |
154 | }) |
155 | .unwrap() |
156 | } |
157 | |
158 | pub fn match_family_style( |
159 | &self, |
160 | family_name: impl AsRef<str>, |
161 | style: FontStyle, |
162 | ) -> Option<Typeface> { |
163 | let family_name = CString::new(family_name.as_ref()).unwrap(); |
164 | Typeface::from_ptr(unsafe { |
165 | sb::C_SkFontMgr_matchFamilyStyle(self.native(), family_name.as_ptr(), style.native()) |
166 | }) |
167 | } |
168 | |
169 | // TODO: support IntoIterator / AsRef<str> for bcp_47? |
170 | pub fn match_family_style_character( |
171 | &self, |
172 | family_name: impl AsRef<str>, |
173 | style: FontStyle, |
174 | bcp_47: &[&str], |
175 | character: Unichar, |
176 | ) -> Option<Typeface> { |
177 | let family_name = CString::new(family_name.as_ref()).unwrap(); |
178 | // create backing store for the pointer array. |
179 | let bcp_47: Vec<CString> = bcp_47.iter().map(|s| CString::new(*s).unwrap()).collect(); |
180 | // note: mutability needed to comply to the C type "const char* bcp47[]". |
181 | let mut bcp_47: Vec<*const c_char> = bcp_47.iter().map(|cs| cs.as_ptr()).collect(); |
182 | |
183 | Typeface::from_ptr(unsafe { |
184 | sb::C_SkFontMgr_matchFamilyStyleCharacter( |
185 | self.native(), |
186 | family_name.as_ptr(), |
187 | style.native(), |
188 | bcp_47.as_mut_ptr(), |
189 | bcp_47.len().try_into().unwrap(), |
190 | character, |
191 | ) |
192 | }) |
193 | } |
194 | |
195 | #[deprecated (since = "0.35.0" , note = "Removed without replacement" )] |
196 | pub fn match_face_style(&self, _typeface: impl AsRef<Typeface>, _style: FontStyle) -> ! { |
197 | panic!("Removed without replacement" ) |
198 | } |
199 | |
200 | pub fn new_from_data( |
201 | &self, |
202 | bytes: &[u8], |
203 | ttc_index: impl Into<Option<usize>>, |
204 | ) -> Option<Typeface> { |
205 | let mut stream = DynamicMemoryWStream::from_bytes(bytes); |
206 | let mut stream = stream.detach_as_stream(); |
207 | Typeface::from_ptr(unsafe { |
208 | let stream_ptr = stream.native_mut() as *mut _; |
209 | // makeFromStream takes ownership of the stream, so don't call drop on it. |
210 | mem::forget(stream); |
211 | sb::C_SkFontMgr_makeFromStream( |
212 | self.native(), |
213 | stream_ptr, |
214 | ttc_index.into().unwrap_or_default().try_into().unwrap(), |
215 | ) |
216 | }) |
217 | } |
218 | |
219 | pub fn legacy_make_typeface<'a>( |
220 | &self, |
221 | family_name: impl Into<Option<&'a str>>, |
222 | style: FontStyle, |
223 | ) -> Option<Typeface> { |
224 | let family_name: Option<CString> = family_name |
225 | .into() |
226 | .and_then(|family_name| CString::new(family_name).ok()); |
227 | |
228 | Typeface::from_ptr(unsafe { |
229 | sb::C_SkFontMgr_legacyMakeTypeface( |
230 | self.native(), |
231 | family_name.map(|n| n.as_ptr()).unwrap_or(ptr::null()), |
232 | style.into_native(), |
233 | ) |
234 | }) |
235 | } |
236 | |
237 | // TODO: makeFromStream(.., ttcIndex). |
238 | } |
239 | |
240 | #[cfg (test)] |
241 | mod tests { |
242 | use crate::FontMgr; |
243 | |
244 | #[test ] |
245 | #[serial_test::serial] |
246 | fn create_all_typefaces() { |
247 | let font_mgr = FontMgr::default(); |
248 | let families = font_mgr.count_families(); |
249 | println!("FontMgr families: {families}" ); |
250 | // This test requires that the default system font manager returns at least one family for now. |
251 | assert!(families > 0); |
252 | // print all family names and styles |
253 | for i in 0..families { |
254 | let name = font_mgr.family_name(i); |
255 | println!("font_family: {name}" ); |
256 | let mut style_set = font_mgr.new_style_set(i); |
257 | for style_index in 0..style_set.count() { |
258 | let (_, style_name) = style_set.style(style_index); |
259 | if let Some(style_name) = style_name { |
260 | println!(" style: {style_name}" ); |
261 | } |
262 | let face = style_set.new_typeface(style_index); |
263 | drop(face); |
264 | } |
265 | } |
266 | } |
267 | } |
268 | |