1use skia_bindings::{self as sb, SkFontMgr, SkFontStyleSet, SkRefCntBase};
2use std::{ffi::CString, fmt, mem, os::raw::c_char, ptr};
3
4use crate::{
5 interop::{self, DynamicMemoryWStream},
6 prelude::*,
7 FontStyle, Typeface, Unichar,
8};
9
10pub type FontStyleSet = RCHandle<SkFontStyleSet>;
11
12impl NativeBase<SkRefCntBase> for SkFontStyleSet {}
13
14impl NativeRefCountedBase for SkFontStyleSet {
15 type Base = SkRefCntBase;
16}
17
18impl Default for FontStyleSet {
19 fn default() -> Self {
20 FontStyleSet::new_empty()
21 }
22}
23
24impl 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
33impl 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
85pub type FontMgr = RCHandle<SkFontMgr>;
86
87impl NativeBase<SkRefCntBase> for SkFontMgr {}
88
89impl NativeRefCountedBase for SkFontMgr {
90 type Base = SkRefCntBase;
91}
92
93impl Default for FontMgr {
94 fn default() -> Self {
95 Self::new()
96 }
97}
98
99impl 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
108impl 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)]
241mod 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