| 1 | use crate::{ |
| 2 | interop::{self, FromStrs, VecSink}, |
| 3 | prelude::*, |
| 4 | textlayout::ParagraphCache, |
| 5 | FontMgr, FontStyle, Typeface, Unichar, |
| 6 | }; |
| 7 | use skia_bindings::{self as sb, skia_textlayout_FontCollection}; |
| 8 | use std::{ffi, fmt, ptr}; |
| 9 | |
| 10 | use super::FontArguments; |
| 11 | |
| 12 | pub type FontCollection = RCHandle<skia_textlayout_FontCollection>; |
| 13 | |
| 14 | impl NativeRefCountedBase for skia_textlayout_FontCollection { |
| 15 | type Base = sb::SkRefCntBase; |
| 16 | } |
| 17 | |
| 18 | impl fmt::Debug for FontCollection { |
| 19 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 20 | f&mut DebugStruct<'_, '_>.debug_struct("FontCollection" ) |
| 21 | .field("font_managers_count" , &self.font_managers_count()) |
| 22 | .field("fallback_manager" , &self.fallback_manager()) |
| 23 | .field("font_fallback_enabled" , &self.font_fallback_enabled()) |
| 24 | .field(name:"paragraph_cache" , &self.paragraph_cache()) |
| 25 | .finish() |
| 26 | } |
| 27 | } |
| 28 | |
| 29 | impl FontCollection { |
| 30 | pub fn new() -> Self { |
| 31 | Self::from_ptr(unsafe { sb::C_FontCollection_new() }).unwrap() |
| 32 | } |
| 33 | |
| 34 | pub fn font_managers_count(&self) -> usize { |
| 35 | unsafe { self.native().getFontManagersCount() } |
| 36 | } |
| 37 | |
| 38 | pub fn set_asset_font_manager(&mut self, font_manager: impl Into<Option<FontMgr>>) { |
| 39 | unsafe { |
| 40 | sb::C_FontCollection_setAssetFontManager( |
| 41 | self.native_mut(), |
| 42 | font_manager.into().into_ptr_or_null(), |
| 43 | ) |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | pub fn set_dynamic_font_manager(&mut self, font_manager: impl Into<Option<FontMgr>>) { |
| 48 | unsafe { |
| 49 | sb::C_FontCollection_setDynamicFontManager( |
| 50 | self.native_mut(), |
| 51 | font_manager.into().into_ptr_or_null(), |
| 52 | ) |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | pub fn set_test_font_manager(&mut self, font_manager: impl Into<Option<FontMgr>>) { |
| 57 | unsafe { |
| 58 | sb::C_FontCollection_setTestFontManager( |
| 59 | self.native_mut(), |
| 60 | font_manager.into().into_ptr_or_null(), |
| 61 | ) |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | pub fn set_default_font_manager<'a>( |
| 66 | &mut self, |
| 67 | font_manager: impl Into<Option<FontMgr>>, |
| 68 | default_family_name: impl Into<Option<&'a str>>, |
| 69 | ) { |
| 70 | let font_manager = font_manager.into(); |
| 71 | unsafe { |
| 72 | match default_family_name.into() { |
| 73 | Some(name) => { |
| 74 | let name = ffi::CString::new(name).unwrap(); |
| 75 | sb::C_FontCollection_setDefaultFontManager2( |
| 76 | self.native_mut(), |
| 77 | font_manager.into_ptr_or_null(), |
| 78 | name.as_ptr(), |
| 79 | ) |
| 80 | } |
| 81 | None => sb::C_FontCollection_setDefaultFontManager( |
| 82 | self.native_mut(), |
| 83 | font_manager.into_ptr_or_null(), |
| 84 | ), |
| 85 | } |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | pub fn set_default_font_manager_and_family_names( |
| 90 | &mut self, |
| 91 | font_manager: impl Into<Option<FontMgr>>, |
| 92 | family_names: &[impl AsRef<str>], |
| 93 | ) { |
| 94 | let font_manager = font_manager.into(); |
| 95 | let family_names = interop::Strings::from_strs(family_names); |
| 96 | unsafe { |
| 97 | sb::C_FontCollection_setDefaultFontManager3( |
| 98 | self.native_mut(), |
| 99 | font_manager.into_ptr_or_null(), |
| 100 | family_names.native(), |
| 101 | ) |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | pub fn fallback_manager(&self) -> Option<FontMgr> { |
| 106 | FontMgr::from_ptr(unsafe { sb::C_FontCollection_getFallbackManager(self.native()) }) |
| 107 | } |
| 108 | |
| 109 | pub fn find_typefaces( |
| 110 | &mut self, |
| 111 | family_names: &[impl AsRef<str>], |
| 112 | font_style: FontStyle, |
| 113 | ) -> Vec<Typeface> { |
| 114 | self.find_typefaces_with_font_arguments(family_names, font_style, None) |
| 115 | } |
| 116 | |
| 117 | pub fn find_typefaces_with_font_arguments<'fa>( |
| 118 | &mut self, |
| 119 | family_names: &[impl AsRef<str>], |
| 120 | font_style: FontStyle, |
| 121 | font_args: impl Into<Option<&'fa FontArguments>>, |
| 122 | ) -> Vec<Typeface> { |
| 123 | let family_names = interop::Strings::from_strs(family_names); |
| 124 | |
| 125 | let mut typefaces: Vec<Typeface> = Vec::new(); |
| 126 | let mut set_typefaces = |tfs: &mut [sb::sk_sp<sb::SkTypeface>]| { |
| 127 | typefaces = tfs |
| 128 | .iter_mut() |
| 129 | .filter_map(|sp| { |
| 130 | let ptr = sp.fPtr; |
| 131 | sp.fPtr = ptr::null_mut(); |
| 132 | Typeface::from_ptr(ptr) |
| 133 | }) |
| 134 | .collect() |
| 135 | }; |
| 136 | |
| 137 | unsafe { |
| 138 | sb::C_FontCollection_findTypefaces( |
| 139 | self.native_mut(), |
| 140 | family_names.native(), |
| 141 | font_style.into_native(), |
| 142 | font_args.into().native_ptr_or_null(), |
| 143 | VecSink::new_mut(&mut set_typefaces).native_mut(), |
| 144 | ) |
| 145 | }; |
| 146 | typefaces |
| 147 | } |
| 148 | |
| 149 | pub fn default_fallback_char( |
| 150 | &mut self, |
| 151 | unicode: Unichar, |
| 152 | font_style: FontStyle, |
| 153 | locale: impl AsRef<str>, |
| 154 | ) -> Option<Typeface> { |
| 155 | let locale = interop::String::from_str(locale.as_ref()); |
| 156 | Typeface::from_ptr(unsafe { |
| 157 | sb::C_FontCollection_defaultFallback( |
| 158 | self.native_mut(), |
| 159 | unicode, |
| 160 | font_style.into_native(), |
| 161 | locale.native(), |
| 162 | ) |
| 163 | }) |
| 164 | } |
| 165 | |
| 166 | pub fn default_fallback(&mut self) -> Option<Typeface> { |
| 167 | Typeface::from_ptr(unsafe { sb::C_FontCollection_defaultFallback2(self.native_mut()) }) |
| 168 | } |
| 169 | |
| 170 | pub fn default_emoji_fallback( |
| 171 | &mut self, |
| 172 | emoji_start: Unichar, |
| 173 | font_style: FontStyle, |
| 174 | locale: impl AsRef<str>, |
| 175 | ) -> Option<Typeface> { |
| 176 | let locale = interop::String::from_str(locale.as_ref()); |
| 177 | Typeface::from_ptr(unsafe { |
| 178 | sb::C_FontCollection_defaultEmojiFallback( |
| 179 | self.native_mut(), |
| 180 | emoji_start, |
| 181 | font_style.into_native(), |
| 182 | locale.native(), |
| 183 | ) |
| 184 | }) |
| 185 | } |
| 186 | |
| 187 | pub fn disable_font_fallback(&mut self) { |
| 188 | unsafe { self.native_mut().disableFontFallback() } |
| 189 | } |
| 190 | |
| 191 | pub fn enable_font_fallback(&mut self) { |
| 192 | unsafe { self.native_mut().enableFontFallback() } |
| 193 | } |
| 194 | |
| 195 | pub fn font_fallback_enabled(&self) -> bool { |
| 196 | unsafe { sb::C_FontCollection_fontFallbackEnabled(self.native()) } |
| 197 | } |
| 198 | |
| 199 | pub fn paragraph_cache(&self) -> &ParagraphCache { |
| 200 | ParagraphCache::from_native_ref(unsafe { |
| 201 | &*sb::C_FontCollection_paragraphCache(self.native_mut_force()) |
| 202 | }) |
| 203 | } |
| 204 | |
| 205 | pub fn paragraph_cache_mut(&mut self) -> &mut ParagraphCache { |
| 206 | ParagraphCache::from_native_ref_mut(unsafe { |
| 207 | &mut *sb::C_FontCollection_paragraphCache(self.native_mut()) |
| 208 | }) |
| 209 | } |
| 210 | |
| 211 | pub fn clear_caches(&mut self) { |
| 212 | unsafe { self.native_mut().clearCaches() } |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | #[cfg (test)] |
| 217 | mod tests { |
| 218 | use crate::prelude::*; |
| 219 | use crate::textlayout::FontCollection; |
| 220 | use crate::{FontMgr, FontStyle}; |
| 221 | |
| 222 | #[test ] |
| 223 | #[serial_test::serial] |
| 224 | fn ref_counts() { |
| 225 | let mut fc = FontCollection::new(); |
| 226 | assert_eq!(fc.native().ref_counted_base()._ref_cnt(), 1); |
| 227 | |
| 228 | let fm = FontMgr::new(); |
| 229 | let fm_base = fm.native().ref_counted_base()._ref_cnt(); |
| 230 | |
| 231 | fc.set_default_font_manager(fm.clone(), None); |
| 232 | assert_eq!(fm.native().ref_counted_base()._ref_cnt(), fm_base + 1); |
| 233 | |
| 234 | let cloned_fc = fc.clone(); |
| 235 | assert_eq!(fm.native().ref_counted_base()._ref_cnt(), fm_base + 1); |
| 236 | assert_eq!(fc.native().ref_counted_base()._ref_cnt(), 2); |
| 237 | drop(cloned_fc); |
| 238 | assert_eq!(fc.native().ref_counted_base()._ref_cnt(), 1); |
| 239 | assert_eq!(fm.native().ref_counted_base()._ref_cnt(), fm_base + 1); |
| 240 | |
| 241 | { |
| 242 | let fmc = fc.fallback_manager().unwrap(); |
| 243 | assert_eq!(fmc.native().ref_counted_base()._ref_cnt(), fm_base + 2); |
| 244 | drop(fmc); |
| 245 | } |
| 246 | |
| 247 | fc.set_default_font_manager(None, None); |
| 248 | assert_eq!(fm.native().ref_counted_base()._ref_cnt(), fm_base); |
| 249 | drop(fm); |
| 250 | drop(fc); |
| 251 | } |
| 252 | |
| 253 | #[test ] |
| 254 | #[serial_test::serial] |
| 255 | fn find_typefaces() { |
| 256 | let mut fc = FontCollection::new(); |
| 257 | fc.set_default_font_manager(FontMgr::new(), None); |
| 258 | println!("find typeface:" ); |
| 259 | for typeface in fc.find_typefaces( |
| 260 | &[ |
| 261 | "Arial" , |
| 262 | "Tahoma" , |
| 263 | "Fira Code" , |
| 264 | "JetBrains Mono" , |
| 265 | "Not Existing" , |
| 266 | ], |
| 267 | FontStyle::default(), |
| 268 | ) { |
| 269 | println!("typeface: {}" , typeface.family_name()); |
| 270 | } |
| 271 | } |
| 272 | } |
| 273 | |