| 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 | |