1use skia_bindings::{self as sb, SkFontMgr, SkFontStyleSet, SkRefCntBase};
2use std::{ffi::CString, fmt, mem, os::raw::c_char, ptr};
3
4use crate::{
5 FontStyle, Typeface, Unichar, font_arguments,
6 interop::{self, DynamicMemoryWStream},
7 prelude::*,
8};
9
10pub mod request {
11 use skia_bindings::{self as sb, SkFontMgr_Request_CMapEntry};
12
13 use crate::{FontStyle, Unichar, font_arguments, prelude::*};
14
15 #[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
16 #[repr(C)]
17 pub struct CMapEntry {
18 pub character: Unichar,
19 pub variation: Unichar,
20 }
21
22 native_transmutable!(SkFontMgr_Request_CMapEntry, CMapEntry);
23
24 pub fn font_style_from_model(
25 model: &[font_arguments::variation_position::Coordinate],
26 ) -> FontStyle {
27 FontStyle::construct(|font_style| unsafe {
28 sb::C_SkFontMgr_Request_fontStyleFromModel(
29 model.native().as_ptr(),
30 model.len(),
31 font_style,
32 )
33 })
34 }
35
36 pub fn model_from_font_style(
37 font_style: FontStyle,
38 ) -> [font_arguments::variation_position::Coordinate; 4] {
39 let mut model = [font_arguments::variation_position::Coordinate::default(); 4];
40 unsafe {
41 sb::C_SkFontMgr_Request_SetModel(font_style.native(), model.native_mut().as_mut_ptr())
42 }
43 model
44 }
45}
46
47#[derive(Clone, Debug, Default)]
48pub struct Request<'a> {
49 pub cmap_entries: &'a [request::CMapEntry],
50 pub bcp_47: &'a [&'a str],
51 pub family_name: Option<&'a str>,
52 pub model: &'a [font_arguments::variation_position::Coordinate],
53 pub synthetic_bold: Option<bool>,
54 pub synthetic_oblique: Option<bool>,
55}
56
57pub type FontStyleSet = RCHandle<SkFontStyleSet>;
58
59impl NativeBase<SkRefCntBase> for SkFontStyleSet {}
60
61impl NativeRefCountedBase for SkFontStyleSet {
62 type Base = SkRefCntBase;
63}
64
65impl Default for FontStyleSet {
66 fn default() -> Self {
67 FontStyleSet::new_empty()
68 }
69}
70
71impl fmt::Debug for FontStyleSet {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 f.debug_struct("FontStyleSet").finish()
74 }
75}
76
77impl FontStyleSet {
78 pub fn count(&mut self) -> usize {
79 unsafe {
80 sb::C_SkFontStyleSet_count(self.native_mut())
81 .try_into()
82 .unwrap()
83 }
84 }
85
86 pub fn style(&mut self, index: usize) -> (FontStyle, Option<String>) {
87 assert!(index < self.count());
88
89 let mut font_style = FontStyle::default();
90 let mut style = interop::String::default();
91 unsafe {
92 sb::C_SkFontStyleSet_getStyle(
93 self.native_mut(),
94 index.try_into().unwrap(),
95 font_style.native_mut(),
96 style.native_mut(),
97 )
98 }
99
100 let style = style.as_str();
101 let name = (!style.is_empty()).then(|| style.into());
103
104 (font_style, name)
105 }
106
107 pub fn new_typeface(&mut self, index: usize) -> Option<Typeface> {
108 assert!(index < self.count());
109
110 Typeface::from_ptr(unsafe {
111 sb::C_SkFontStyleSet_createTypeface(self.native_mut(), index.try_into().unwrap())
112 })
113 }
114
115 pub fn match_style(&mut self, pattern: FontStyle) -> Option<Typeface> {
116 Typeface::from_ptr(unsafe {
117 sb::C_SkFontStyleSet_matchStyle(self.native_mut(), pattern.native())
118 })
119 }
120
121 pub fn new_empty() -> Self {
122 FontStyleSet::from_ptr(unsafe { sb::C_SkFontStyleSet_CreateEmpty() }).unwrap()
123 }
124}
125
126pub type FontMgr = RCHandle<SkFontMgr>;
127
128impl NativeBase<SkRefCntBase> for SkFontMgr {}
129
130impl NativeRefCountedBase for SkFontMgr {
131 type Base = SkRefCntBase;
132}
133
134impl Default for FontMgr {
135 fn default() -> Self {
136 Self::new()
137 }
138}
139
140impl fmt::Debug for FontMgr {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142 let names: Vec<_> = self.family_names().collect();
143 f.debug_struct("FontMgr")
144 .field("family_names", &names)
145 .finish()
146 }
147}
148
149impl FontMgr {
150 pub fn new() -> Self {
153 FontMgr::from_ptr(unsafe { sb::C_SkFontMgr_NewSystem() }).unwrap()
154 }
155
156 pub fn empty() -> Self {
157 FontMgr::from_ptr(unsafe { sb::C_SkFontMgr_RefEmpty() }).unwrap()
158 }
159
160 pub fn custom_empty() -> Option<Self> {
164 FontMgr::from_ptr(unsafe { sb::C_SkFontMgr_NewCustomEmpty() })
165 }
166
167 pub fn count_families(&self) -> usize {
168 unsafe { self.native().countFamilies().try_into().unwrap() }
169 }
170
171 pub fn family_name(&self, index: usize) -> String {
172 assert!(index < self.count_families());
173 let mut family_name = interop::String::default();
174 unsafe {
175 self.native()
176 .getFamilyName(index.try_into().unwrap(), family_name.native_mut())
177 }
178 family_name.as_str().into()
179 }
180
181 pub fn family_names(&self) -> impl Iterator<Item = String> + use<'_> {
182 (0..self.count_families()).map(move |i| self.family_name(i))
183 }
184
185 #[deprecated(since = "0.41.0", note = "Use new_style_set")]
186 pub fn new_styleset(&self, index: usize) -> FontStyleSet {
187 self.new_style_set(index)
188 }
189
190 pub fn new_style_set(&self, index: usize) -> FontStyleSet {
191 assert!(index < self.count_families());
192 FontStyleSet::from_ptr(unsafe {
193 sb::C_SkFontMgr_createStyleSet(self.native(), index.try_into().unwrap())
194 })
195 .unwrap()
196 }
197
198 pub fn match_family(&self, family_name: impl AsRef<str>) -> FontStyleSet {
199 let family_name = CString::new(family_name.as_ref()).unwrap();
200 FontStyleSet::from_ptr(unsafe {
201 sb::C_SkFontMgr_matchFamily(self.native(), family_name.as_ptr())
202 })
203 .unwrap()
204 }
205
206 pub fn match_family_style(
207 &self,
208 family_name: impl AsRef<str>,
209 style: FontStyle,
210 ) -> Option<Typeface> {
211 let family_name = CString::new(family_name.as_ref()).unwrap();
212 Typeface::from_ptr(unsafe {
213 sb::C_SkFontMgr_matchFamilyStyle(self.native(), family_name.as_ptr(), style.native())
214 })
215 }
216
217 pub fn match_family_style_character(
219 &self,
220 family_name: impl AsRef<str>,
221 style: FontStyle,
222 bcp_47: &[&str],
223 character: Unichar,
224 ) -> Option<Typeface> {
225 let family_name = CString::new(family_name.as_ref()).unwrap();
226 let bcp_47: Vec<CString> = bcp_47.iter().map(|s| CString::new(*s).unwrap()).collect();
228 let mut bcp_47: Vec<*const c_char> = bcp_47.iter().map(|cs| cs.as_ptr()).collect();
230
231 Typeface::from_ptr(unsafe {
232 sb::C_SkFontMgr_matchFamilyStyleCharacter(
233 self.native(),
234 family_name.as_ptr(),
235 style.native(),
236 bcp_47.as_mut_ptr(),
237 bcp_47.len().try_into().unwrap(),
238 character,
239 )
240 })
241 }
242
243 pub fn match_request(&self, request: &Request<'_>) -> Option<Typeface> {
244 with_ffi_request(request, |ffi_request| {
245 Typeface::from_ptr(unsafe { sb::C_SkFontMgr_match(self.native(), ffi_request) })
246 })
247 }
248
249 pub fn fallback(&self, request: &Request<'_>) -> Option<Typeface> {
250 with_ffi_request(request, |ffi_request| {
251 Typeface::from_ptr(unsafe { sb::C_SkFontMgr_fallback(self.native(), ffi_request) })
252 })
253 }
254
255 pub fn fallback_request(&self, request: &Request<'_>) -> Option<Typeface> {
256 self.fallback(request)
257 }
258
259 #[deprecated(since = "0.35.0", note = "Removed without replacement")]
260 pub fn match_face_style(&self, _typeface: impl AsRef<Typeface>, _style: FontStyle) -> ! {
261 panic!("Removed without replacement")
262 }
263
264 pub fn new_from_data(
280 &self,
281 bytes: &[u8],
282 ttc_index: impl Into<Option<usize>>,
283 ) -> Option<Typeface> {
284 let mut stream = DynamicMemoryWStream::from_bytes(bytes);
285 let mut stream = stream.detach_as_stream();
286 Typeface::from_ptr(unsafe {
287 let stream_ptr = stream.native_mut() as *mut _;
288 mem::forget(stream);
290 sb::C_SkFontMgr_makeFromStream(
291 self.native(),
292 stream_ptr,
293 ttc_index.into().unwrap_or_default().try_into().unwrap(),
294 )
295 })
296 }
297
298 pub fn legacy_make_typeface<'a>(
299 &self,
300 family_name: impl Into<Option<&'a str>>,
301 style: FontStyle,
302 ) -> Option<Typeface> {
303 let family_name: Option<CString> = family_name
304 .into()
305 .and_then(|family_name| CString::new(family_name).ok());
306
307 Typeface::from_ptr(unsafe {
308 sb::C_SkFontMgr_legacyMakeTypeface(
309 self.native(),
310 family_name
311 .as_ref()
312 .map(|n| n.as_ptr())
313 .unwrap_or(ptr::null()),
314 style.into_native(),
315 )
316 })
317 }
318
319 }
321
322fn with_ffi_request<T>(request: &Request<'_>, f: impl FnOnce(&sb::C_SkFontMgr_Request) -> T) -> T {
323 let family_name = request
324 .family_name
325 .and_then(|family_name| CString::new(family_name).ok());
326 let bcp_47: Vec<CString> = request
327 .bcp_47
328 .iter()
329 .map(|s| CString::new(*s).unwrap())
330 .collect();
331 let mut bcp_47_ptrs: Vec<*const c_char> = bcp_47.iter().map(|cs| cs.as_ptr()).collect();
332 let bcp_47_ptr = if bcp_47_ptrs.is_empty() {
333 ptr::null_mut()
334 } else {
335 bcp_47_ptrs.as_mut_ptr()
336 };
337
338 let ffi_request = sb::C_SkFontMgr_Request {
339 cmapEntries: request.cmap_entries.native().as_ptr(),
340 cmapEntryCount: request.cmap_entries.len(),
341 bcp47: bcp_47_ptr,
342 bcp47Count: bcp_47_ptrs.len(),
343 familyName: family_name
344 .as_ref()
345 .map(|n| n.as_ptr())
346 .unwrap_or(ptr::null()),
347 model: request.model.native().as_ptr(),
348 modelCount: request.model.len(),
349 syntheticBold: option_bool_to_ffi(request.synthetic_bold),
350 syntheticOblique: option_bool_to_ffi(request.synthetic_oblique),
351 };
352
353 f(&ffi_request)
354}
355
356fn option_bool_to_ffi(value: Option<bool>) -> i32 {
357 match value {
358 Some(true) => 1,
359 Some(false) => 0,
360 None => -1,
361 }
362}
363
364#[cfg(test)]
365mod tests {
366 use crate::{
367 FontMgr, FontStyle,
368 font_mgr::{Request, request},
369 };
370
371 #[test]
372 #[serial_test::serial]
373 fn create_all_typefaces() {
374 let font_mgr = FontMgr::default();
375 let families = font_mgr.count_families();
376 println!("FontMgr families: {families}");
377 assert!(families > 0);
379 for i in 0..families {
381 let name = font_mgr.family_name(i);
382 println!("font_family: {name}");
383 let mut style_set = font_mgr.new_style_set(i);
384 for style_index in 0..style_set.count() {
385 let (_, style_name) = style_set.style(style_index);
386 if let Some(style_name) = style_name {
387 println!(" style: {style_name}");
388 }
389 let face = style_set.new_typeface(style_index);
390 drop(face);
391 }
392 }
393 }
394
395 #[test]
396 fn request_apis_accept_default_request() {
397 let font_mgr = FontMgr::empty();
398 let request = Request::default();
399
400 let _ = font_mgr.match_request(&request);
401 let _ = font_mgr.fallback(&request);
402
403 let _ = request::font_style_from_model(&[]);
404 let _ = request::model_from_font_style(FontStyle::default());
405 }
406}