fonts/platform/freetype/
freetype_face.rs1use std::ffi::{CStr, c_long};
6use std::ptr;
7
8use app_units::Au;
9use freetype_sys::{
10 FT_Done_Face, FT_Done_MM_Var, FT_F26Dot6, FT_FACE_FLAG_COLOR, FT_FACE_FLAG_FIXED_SIZES,
11 FT_FACE_FLAG_SCALABLE, FT_Face, FT_FaceRec, FT_Fixed, FT_Get_MM_Var, FT_HAS_MULTIPLE_MASTERS,
12 FT_Int32, FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_Long, FT_MM_Var, FT_New_Face, FT_New_Memory_Face,
13 FT_Pos, FT_Select_Size, FT_Set_Char_Size, FT_Set_Var_Design_Coordinates, FT_UInt,
14 FTErrorMethods,
15};
16use webrender_api::FontVariation;
17
18use crate::platform::freetype::library_handle::FreeTypeLibraryHandle;
19
20const FT_LOAD_TARGET_LIGHT: FT_UInt = 1 << 16;
24
25#[derive(Debug)]
27pub(crate) struct FreeTypeFace {
28 face: ptr::NonNull<FT_FaceRec>,
32}
33
34impl FreeTypeFace {
35 pub(crate) fn new_from_memory(
36 library: &FreeTypeLibraryHandle,
37 data: &[u8],
38 ) -> Result<Self, &'static str> {
39 let mut face = ptr::null_mut();
40 let result = unsafe {
41 FT_New_Memory_Face(
42 library.freetype_library,
43 data.as_ptr(),
44 data.len() as FT_Long,
45 0,
46 &mut face,
47 )
48 };
49
50 if 0 != result {
51 return Err("Could not create FreeType face");
52 }
53 let Some(face) = ptr::NonNull::new(face) else {
54 return Err("Could not create FreeType face");
55 };
56
57 Ok(Self { face })
58 }
59
60 pub(crate) fn new_from_file(
61 library: &FreeTypeLibraryHandle,
62 filename: &CStr,
63 index: u32,
64 ) -> Result<Self, &'static str> {
65 let mut face = ptr::null_mut();
66 let result = unsafe {
67 FT_New_Face(
68 library.freetype_library,
69 filename.as_ptr(),
70 index as FT_Long,
71 &mut face,
72 )
73 };
74
75 if 0 != result {
76 return Err("Could not create FreeType face");
77 }
78 let Some(face) = ptr::NonNull::new(face) else {
79 return Err("Could not create FreeType face");
80 };
81
82 Ok(Self { face })
83 }
84
85 pub(crate) fn as_ref(&self) -> &FT_FaceRec {
86 unsafe { self.face.as_ref() }
87 }
88
89 pub(crate) fn as_ptr(&self) -> FT_Face {
90 self.face.as_ptr()
91 }
92
93 pub(crate) fn scalable(&self) -> bool {
95 self.as_ref().face_flags & FT_FACE_FLAG_SCALABLE as c_long != 0
96 }
97
98 pub(crate) fn color(&self) -> bool {
100 self.as_ref().face_flags & FT_FACE_FLAG_COLOR as c_long != 0
101 }
102
103 pub(crate) fn set_size(&self, requested_size: Au) -> Result<Au, &'static str> {
108 if self.scalable() {
109 let size_in_fixed_point = (requested_size.to_f64_px() * 64.0 + 0.5) as FT_F26Dot6;
110 let result =
111 unsafe { FT_Set_Char_Size(self.face.as_ptr(), size_in_fixed_point, 0, 72, 72) };
112 if 0 != result {
113 return Err("FT_Set_Char_Size failed");
114 }
115 return Ok(requested_size);
116 }
117
118 let requested_size = (requested_size.to_f64_px() * 64.0) as FT_Pos;
119 let get_size_at_index = |index| unsafe {
120 (
121 (*self.as_ref().available_sizes.offset(index as isize)).x_ppem,
122 (*self.as_ref().available_sizes.offset(index as isize)).y_ppem,
123 )
124 };
125
126 let mut best_index = 0;
127 let mut best_size = get_size_at_index(0);
128 let mut best_dist = best_size.1 - requested_size;
129 for strike_index in 1..self.as_ref().num_fixed_sizes {
130 let new_scale = get_size_at_index(strike_index);
131 let new_distance = new_scale.1 - requested_size;
132
133 if (best_dist < 0 && new_distance >= best_dist) || new_distance.abs() <= best_dist {
137 best_dist = new_distance;
138 best_size = new_scale;
139 best_index = strike_index;
140 }
141 }
142
143 if 0 == unsafe { FT_Select_Size(self.face.as_ptr(), best_index) } {
144 Ok(Au::from_f64_px(best_size.1 as f64 / 64.0))
145 } else {
146 Err("FT_Select_Size failed")
147 }
148 }
149
150 pub(crate) fn glyph_load_flags(&self) -> FT_Int32 {
152 let mut load_flags = FT_LOAD_DEFAULT;
153
154 load_flags |= FT_LOAD_TARGET_LIGHT as i32;
159
160 let face_flags = self.as_ref().face_flags;
161 if (face_flags & (FT_FACE_FLAG_FIXED_SIZES as FT_Long)) != 0 {
162 load_flags |= FT_LOAD_COLOR;
166 }
167
168 load_flags as FT_Int32
169 }
170
171 pub(crate) fn set_variations_for_font(
177 &self,
178 variations: &[FontVariation],
179 library: &FreeTypeLibraryHandle,
180 ) -> Result<Vec<FontVariation>, &'static str> {
181 if !FT_HAS_MULTIPLE_MASTERS(self.as_ptr()) ||
182 variations.is_empty() ||
183 !servo_config::pref!(layout_variable_fonts_enabled)
184 {
185 return Ok(vec![]);
187 }
188
189 let mut mm_var: *mut FT_MM_Var = ptr::null_mut();
191 let result = unsafe { FT_Get_MM_Var(self.as_ptr(), &mut mm_var as *mut _) };
192 if !result.succeeded() {
193 return Err("Failed to query font variations");
194 }
195
196 let num_axis = unsafe { (*mm_var).num_axis } as usize;
199 let mut normalized_axis_values = Vec::with_capacity(variations.len());
200 let mut coords = vec![0; num_axis];
201 for (index, coord) in coords.iter_mut().enumerate() {
202 let axis_data = unsafe { &*(*mm_var).axis.add(index) };
203 let Some(variation) = variations
204 .iter()
205 .find(|variation| variation.tag == axis_data.tag as u32)
206 else {
207 *coord = axis_data.def;
208 continue;
209 };
210
211 let shift_factor = 16.0_f32.exp2();
213 let min_value = axis_data.minimum as f32 / shift_factor;
214 let max_value = axis_data.maximum as f32 / shift_factor;
215 normalized_axis_values.push(FontVariation {
216 tag: variation.tag,
217 value: variation.value.min(max_value).max(min_value),
218 });
219
220 *coord = (variation.value * shift_factor) as FT_Fixed;
221 }
222
223 unsafe {
225 FT_Done_MM_Var(library.freetype_library, mm_var);
226 }
227
228 let result = unsafe {
230 FT_Set_Var_Design_Coordinates(self.as_ptr(), coords.len() as u32, coords.as_ptr())
231 };
232 if !result.succeeded() {
233 return Err("Could not set variations for font face");
234 }
235
236 Ok(normalized_axis_values)
237 }
238}
239
240unsafe impl Send for FreeTypeFace {}
243
244impl Drop for FreeTypeFace {
245 fn drop(&mut self) {
246 let _guard = FreeTypeLibraryHandle::get().lock();
250 if unsafe { FT_Done_Face(self.face.as_ptr()) } != 0 {
251 panic!("FT_Done_Face failed");
252 }
253 }
254}