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 face = self.as_ref();
119 if face.num_fixed_sizes <= 0 || face.available_sizes.is_null() {
120 return Err("No fixed sizes available");
121 }
122
123 let requested_size = (requested_size.to_f64_px() * 64.0) as FT_Pos;
124 let get_size_at_index = |index| {
125 assert!(index < face.num_fixed_sizes);
126 unsafe {
128 (
129 (*face.available_sizes.offset(index as isize)).x_ppem,
130 (*face.available_sizes.offset(index as isize)).y_ppem,
131 )
132 }
133 };
134
135 let mut best_index = 0;
136 let mut best_size = get_size_at_index(0);
137 let mut best_dist = best_size.1 - requested_size;
138 for strike_index in 1..face.num_fixed_sizes {
139 let new_scale = get_size_at_index(strike_index);
140 let new_distance = new_scale.1 - requested_size;
141
142 if (best_dist < 0 && new_distance >= best_dist) || new_distance.abs() <= best_dist {
146 best_dist = new_distance;
147 best_size = new_scale;
148 best_index = strike_index;
149 }
150 }
151
152 if 0 == unsafe { FT_Select_Size(self.face.as_ptr(), best_index) } {
153 Ok(Au::from_f64_px(best_size.1 as f64 / 64.0))
154 } else {
155 Err("FT_Select_Size failed")
156 }
157 }
158
159 pub(crate) fn glyph_load_flags(&self) -> FT_Int32 {
161 let mut load_flags = FT_LOAD_DEFAULT;
162
163 load_flags |= FT_LOAD_TARGET_LIGHT as i32;
168
169 let face_flags = self.as_ref().face_flags;
170 if (face_flags & (FT_FACE_FLAG_FIXED_SIZES as FT_Long)) != 0 {
171 load_flags |= FT_LOAD_COLOR;
175 }
176
177 load_flags as FT_Int32
178 }
179
180 pub(crate) fn set_variations_for_font(
186 &self,
187 variations: &[FontVariation],
188 library: &FreeTypeLibraryHandle,
189 ) -> Result<Vec<FontVariation>, &'static str> {
190 if !FT_HAS_MULTIPLE_MASTERS(self.as_ptr()) ||
191 variations.is_empty() ||
192 !servo_config::pref!(layout_variable_fonts_enabled)
193 {
194 return Ok(vec![]);
196 }
197
198 let mut mm_var: *mut FT_MM_Var = ptr::null_mut();
200 let result = unsafe { FT_Get_MM_Var(self.as_ptr(), &mut mm_var as *mut _) };
201 if !result.succeeded() {
202 return Err("Failed to query font variations");
203 }
204
205 let num_axis = unsafe { (*mm_var).num_axis } as usize;
208 let mut normalized_axis_values = Vec::with_capacity(variations.len());
209 let mut coords = vec![0; num_axis];
210 for (index, coord) in coords.iter_mut().enumerate() {
211 let axis_data = unsafe { &*(*mm_var).axis.add(index) };
212 let Some(variation) = variations
213 .iter()
214 .find(|variation| variation.tag == axis_data.tag as u32)
215 else {
216 *coord = axis_data.def;
217 continue;
218 };
219
220 let shift_factor = 16.0_f32.exp2();
222 let min_value = axis_data.minimum as f32 / shift_factor;
223 let max_value = axis_data.maximum as f32 / shift_factor;
224 normalized_axis_values.push(FontVariation {
225 tag: variation.tag,
226 value: variation.value.min(max_value).max(min_value),
227 });
228
229 *coord = (variation.value * shift_factor) as FT_Fixed;
230 }
231
232 unsafe {
234 FT_Done_MM_Var(library.freetype_library, mm_var);
235 }
236
237 let result = unsafe {
239 FT_Set_Var_Design_Coordinates(self.as_ptr(), coords.len() as u32, coords.as_ptr())
240 };
241 if !result.succeeded() {
242 return Err("Could not set variations for font face");
243 }
244
245 Ok(normalized_axis_values)
246 }
247}
248
249unsafe impl Send for FreeTypeFace {}
252
253impl Drop for FreeTypeFace {
254 fn drop(&mut self) {
255 let _guard = FreeTypeLibraryHandle::get().lock();
259 if unsafe { FT_Done_Face(self.face.as_ptr()) } != 0 {
260 log::error!("FT_Done_Face failed, leaking memory");
261 }
262 }
263}