fonts/platform/freetype/
freetype_face.rs1use std::ffi::c_long;
6use std::fmt::Debug;
7use std::ptr;
8
9use app_units::Au;
10use fonts_traits::FontData;
11use freetype_sys::{
12 FT_Done_Face, FT_Done_MM_Var, FT_F26Dot6, FT_FACE_FLAG_COLOR, FT_FACE_FLAG_FIXED_SIZES,
13 FT_FACE_FLAG_SCALABLE, FT_Face, FT_FaceRec, FT_Fixed, FT_Get_MM_Var, FT_HAS_MULTIPLE_MASTERS,
14 FT_Int32, FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_LOAD_NO_HINTING, FT_LOAD_TARGET_LIGHT,
15 FT_LOAD_TARGET_MONO, FT_LOAD_TARGET_NORMAL, FT_Long, FT_MM_Var, FT_New_Memory_Face, FT_Pos,
16 FT_Select_Size, FT_Set_Char_Size, FT_Set_Var_Design_Coordinates, FTErrorMethods,
17};
18use memmap2::Mmap;
19use servo_arc::Arc;
20use webrender_api::{FontHinting, FontVariation};
21
22use crate::platform::freetype::library_handle::FreeTypeLibraryHandle;
23
24#[cfg(not(any(target_os = "android", target_env = "ohos")))]
30pub(crate) const FALLBACK_HINTING_STYLE: FontHinting = FontHinting::Light;
31
32#[cfg(any(target_os = "android", target_env = "ohos"))]
38pub(crate) const FALLBACK_HINTING_STYLE: FontHinting = FontHinting::None;
39
40fn fallback_free_type_hinting_load_flags() -> FT_Int32 {
41 match FALLBACK_HINTING_STYLE {
42 FontHinting::None => FT_LOAD_NO_HINTING,
43 FontHinting::Mono => FT_LOAD_TARGET_MONO,
44 FontHinting::Light => FT_LOAD_TARGET_LIGHT,
45 FontHinting::LCD | FontHinting::Normal => FT_LOAD_TARGET_NORMAL,
49 }
50}
51
52#[derive(Debug)]
54pub(crate) struct FreeTypeFace {
55 face: ptr::NonNull<FT_FaceRec>,
59 _data: FontBackingStore,
60}
61
62pub(crate) enum FontBackingStore {
63 Web(FontData),
64 Local(Arc<Mmap>),
66}
67
68impl AsRef<[u8]> for FontBackingStore {
69 fn as_ref(&self) -> &[u8] {
70 match self {
71 Self::Web(font_data) => font_data.as_ref(),
72 Self::Local(mmap) => mmap.as_ref(),
73 }
74 }
75}
76
77impl Debug for FontBackingStore {
78 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
79 f.debug_struct("FontBackingStore")
80 .field("len", &self.as_ref().len())
81 .finish()
82 }
83}
84
85impl FreeTypeFace {
86 pub(crate) fn new_from_memory(
87 library: &FreeTypeLibraryHandle,
88 font_backing_store: FontBackingStore,
89 face_index: u32,
90 ) -> Result<Self, &'static str> {
91 let mut face = ptr::null_mut();
92 let data = font_backing_store.as_ref();
93 let result = unsafe {
97 FT_New_Memory_Face(
98 library.freetype_library,
99 data.as_ptr(),
100 data.len() as FT_Long,
101 face_index as FT_Long,
102 &mut face,
103 )
104 };
105
106 if 0 != result {
107 return Err("Could not create FreeType face");
108 }
109 let Some(face) = ptr::NonNull::new(face) else {
110 return Err("Could not create FreeType face");
111 };
112
113 Ok(Self {
114 face,
115 _data: font_backing_store,
116 })
117 }
118
119 pub(crate) fn as_ref(&self) -> &FT_FaceRec {
120 unsafe { self.face.as_ref() }
121 }
122
123 pub(crate) fn as_ptr(&self) -> FT_Face {
124 self.face.as_ptr()
125 }
126
127 pub(crate) fn scalable(&self) -> bool {
129 self.as_ref().face_flags & FT_FACE_FLAG_SCALABLE as c_long != 0
130 }
131
132 pub(crate) fn color(&self) -> bool {
134 self.as_ref().face_flags & FT_FACE_FLAG_COLOR as c_long != 0
135 }
136
137 pub(crate) fn set_size(&self, requested_size: Au) -> Result<Au, &'static str> {
142 if self.scalable() {
143 let size_in_fixed_point = (requested_size.to_f64_px() * 64.0 + 0.5) as FT_F26Dot6;
144 let result =
145 unsafe { FT_Set_Char_Size(self.face.as_ptr(), size_in_fixed_point, 0, 72, 72) };
146 if 0 != result {
147 return Err("FT_Set_Char_Size failed");
148 }
149 return Ok(requested_size);
150 }
151
152 let face = self.as_ref();
153 if face.num_fixed_sizes <= 0 || face.available_sizes.is_null() {
154 return Err("No fixed sizes available");
155 }
156
157 let requested_size = (requested_size.to_f64_px() * 64.0) as FT_Pos;
158 let get_size_at_index = |index| {
159 assert!(index < face.num_fixed_sizes);
160 unsafe {
162 (
163 (*face.available_sizes.offset(index as isize)).x_ppem,
164 (*face.available_sizes.offset(index as isize)).y_ppem,
165 )
166 }
167 };
168
169 let mut best_index = 0;
170 let mut best_size = get_size_at_index(0);
171 let mut best_dist = best_size.1 - requested_size;
172 for strike_index in 1..face.num_fixed_sizes {
173 let new_scale = get_size_at_index(strike_index);
174 let new_distance = new_scale.1 - requested_size;
175
176 if (best_dist < 0 && new_distance >= best_dist) || new_distance.abs() <= best_dist {
180 best_dist = new_distance;
181 best_size = new_scale;
182 best_index = strike_index;
183 }
184 }
185
186 if 0 == unsafe { FT_Select_Size(self.face.as_ptr(), best_index) } {
187 Ok(Au::from_f64_px(best_size.1 as f64 / 64.0))
188 } else {
189 Err("FT_Select_Size failed")
190 }
191 }
192
193 pub(crate) fn glyph_load_flags(&self) -> FT_Int32 {
195 let mut load_flags = FT_LOAD_DEFAULT | fallback_free_type_hinting_load_flags();
196 let face_flags = self.as_ref().face_flags;
197 if (face_flags & (FT_FACE_FLAG_FIXED_SIZES as FT_Long)) != 0 {
198 load_flags |= FT_LOAD_COLOR;
202 }
203
204 load_flags as FT_Int32
205 }
206
207 pub(crate) fn set_variations_for_font(
213 &self,
214 variations: &[FontVariation],
215 library: &FreeTypeLibraryHandle,
216 ) -> Result<Vec<FontVariation>, &'static str> {
217 if !unsafe { FT_HAS_MULTIPLE_MASTERS(self.as_ptr()) } ||
218 variations.is_empty() ||
219 !servo_config::pref!(layout_variable_fonts_enabled)
220 {
221 return Ok(vec![]);
223 }
224
225 let mut mm_var: *mut FT_MM_Var = ptr::null_mut();
227 let result = unsafe { FT_Get_MM_Var(self.as_ptr(), &mut mm_var as *mut _) };
228 if !result.succeeded() {
229 return Err("Failed to query font variations");
230 }
231
232 let num_axis = unsafe { (*mm_var).num_axis } as usize;
235 let mut normalized_axis_values = Vec::with_capacity(variations.len());
236 let mut coords = vec![0; num_axis];
237 for (index, coord) in coords.iter_mut().enumerate() {
238 let axis_data = unsafe { &*(*mm_var).axis.add(index) };
239 let Some(variation) = variations
240 .iter()
241 .find(|variation| variation.tag == axis_data.tag as u32)
242 else {
243 *coord = axis_data.def;
244 continue;
245 };
246
247 let shift_factor = 16.0_f32.exp2();
249 let min_value = axis_data.minimum as f32 / shift_factor;
250 let max_value = axis_data.maximum as f32 / shift_factor;
251 normalized_axis_values.push(FontVariation {
252 tag: variation.tag,
253 value: variation.value.min(max_value).max(min_value),
254 });
255
256 *coord = (variation.value * shift_factor) as FT_Fixed;
257 }
258
259 unsafe {
261 FT_Done_MM_Var(library.freetype_library, mm_var);
262 }
263
264 let result = unsafe {
266 FT_Set_Var_Design_Coordinates(self.as_ptr(), coords.len() as u32, coords.as_ptr())
267 };
268 if !result.succeeded() {
269 return Err("Could not set variations for font face");
270 }
271
272 Ok(normalized_axis_values)
273 }
274}
275
276unsafe impl Send for FreeTypeFace {}
279
280impl Drop for FreeTypeFace {
281 fn drop(&mut self) {
282 let result_code = {
286 let _guard = FreeTypeLibraryHandle::get().lock();
287 unsafe { FT_Done_Face(self.face.as_ptr()) }
290 };
291 if result_code != 0 {
292 log::error!("FT_Done_Face failed: {result_code}");
293 }
294 }
295}