1use std::ffi::CString;
6use std::fs::File;
7
8use app_units::Au;
9use euclid::default::{Point2D, Rect, Size2D};
10use fonts_traits::{FontIdentifier, FontTemplateDescriptor, LocalFontIdentifier};
11use freetype_sys::{
12 FT_F26Dot6, FT_Get_Char_Index, FT_Get_Kerning, FT_GlyphSlot, FT_KERNING_DEFAULT,
13 FT_LOAD_DEFAULT, FT_LOAD_NO_HINTING, FT_Load_Glyph, FT_Size_Metrics, FT_SizeRec, FT_UInt,
14 FT_ULong, FT_Vector,
15};
16use log::debug;
17use memmap2::Mmap;
18use parking_lot::ReentrantMutex;
19use read_fonts::types::Tag;
20use read_fonts::{FontRef, ReadError, TableProvider};
21use servo_arc::Arc;
22use style::Zero;
23use webrender_api::{FontInstanceFlags, FontVariation};
24
25use super::library_handle::FreeTypeLibraryHandle;
26use crate::FontData;
27use crate::font::{FontMetrics, FontTableMethods, FractionalPixel, PlatformFontMethods};
28use crate::glyph::GlyphId;
29use crate::platform::freetype::freetype_face::FreeTypeFace;
30
31fn fixed_26_dot_6_to_float(fixed: FT_F26Dot6) -> f64 {
33 fixed as f64 / 64.0
34}
35
36#[derive(Debug)]
37pub struct FontTable {
38 data: FreeTypeFaceTableProviderData,
39 tag: Tag,
40}
41
42impl FontTableMethods for FontTable {
43 fn buffer(&self) -> &[u8] {
44 let font_ref = self.data.font_ref().expect("Font checked before creating");
45 let table_data = font_ref
46 .table_data(self.tag)
47 .expect("Table existence checked before creating");
48 table_data.as_bytes()
49 }
50}
51
52#[derive(Debug)]
53#[allow(unused)]
54pub struct PlatformFont {
55 face: ReentrantMutex<FreeTypeFace>,
56 requested_face_size: Au,
57 actual_face_size: Au,
58 variations: Vec<FontVariation>,
59
60 table_provider_data: FreeTypeFaceTableProviderData,
62}
63
64impl PlatformFontMethods for PlatformFont {
65 fn new_from_data(
66 _font_identifier: FontIdentifier,
67 font_data: &FontData,
68 requested_size: Option<Au>,
69 variations: &[FontVariation],
70 ) -> Result<PlatformFont, &'static str> {
71 let library = FreeTypeLibraryHandle::get().lock();
72 let data: &[u8] = font_data.as_ref();
73 let face = FreeTypeFace::new_from_memory(&library, data)?;
74
75 let normalized_variations = face.set_variations_for_font(variations, &library)?;
76
77 let (requested_face_size, actual_face_size) = match requested_size {
78 Some(requested_size) => (requested_size, face.set_size(requested_size)?),
79 None => (Au::zero(), Au::zero()),
80 };
81
82 Ok(PlatformFont {
83 face: ReentrantMutex::new(face),
84 requested_face_size,
85 actual_face_size,
86 table_provider_data: FreeTypeFaceTableProviderData::Web(font_data.clone()),
87 variations: normalized_variations,
88 })
89 }
90
91 fn new_from_local_font_identifier(
92 font_identifier: LocalFontIdentifier,
93 requested_size: Option<Au>,
94 variations: &[FontVariation],
95 ) -> Result<PlatformFont, &'static str> {
96 let library = FreeTypeLibraryHandle::get().lock();
97 let filename = CString::new(&*font_identifier.path).expect("filename contains NUL byte!");
98
99 let face = FreeTypeFace::new_from_file(&library, &filename, font_identifier.index())?;
100
101 let normalized_variations = face.set_variations_for_font(variations, &library)?;
102
103 let (requested_face_size, actual_face_size) = match requested_size {
104 Some(requested_size) => (requested_size, face.set_size(requested_size)?),
105 None => (Au::zero(), Au::zero()),
106 };
107
108 let Ok(memory_mapped_font_data) =
109 File::open(&*font_identifier.path).and_then(|file| unsafe { Mmap::map(&file) })
110 else {
111 return Err("Could not memory map");
112 };
113
114 Ok(PlatformFont {
115 face: ReentrantMutex::new(face),
116 requested_face_size,
117 actual_face_size,
118 table_provider_data: FreeTypeFaceTableProviderData::Local(
119 Arc::new(memory_mapped_font_data),
120 font_identifier.index(),
121 ),
122 variations: normalized_variations,
123 })
124 }
125
126 fn descriptor(&self) -> FontTemplateDescriptor {
127 let Ok(font_ref) = self.table_provider_data.font_ref() else {
128 return FontTemplateDescriptor::default();
129 };
130 let Ok(os2) = font_ref.os2() else {
131 return FontTemplateDescriptor::default();
132 };
133 Self::descriptor_from_os2_table(&os2)
134 }
135
136 fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
137 let face = self.face.lock();
138
139 unsafe {
140 let idx = FT_Get_Char_Index(face.as_ptr(), codepoint as FT_ULong);
141 if idx != 0 as FT_UInt {
142 Some(idx as GlyphId)
143 } else {
144 debug!(
145 "Invalid codepoint: U+{:04X} ('{}')",
146 codepoint as u32, codepoint
147 );
148 None
149 }
150 }
151 }
152
153 fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId) -> FractionalPixel {
154 let face = self.face.lock();
155
156 let mut delta = FT_Vector { x: 0, y: 0 };
157 unsafe {
158 FT_Get_Kerning(
159 face.as_ptr(),
160 first_glyph,
161 second_glyph,
162 FT_KERNING_DEFAULT,
163 &mut delta,
164 );
165 }
166 fixed_26_dot_6_to_float(delta.x) * self.unscalable_font_metrics_scale()
167 }
168
169 fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
170 let face = self.face.lock();
171
172 let load_flags = face.glyph_load_flags();
173 let result = unsafe { FT_Load_Glyph(face.as_ptr(), glyph as FT_UInt, load_flags) };
174 if 0 != result {
175 debug!("Unable to load glyph {}. reason: {:?}", glyph, result);
176 return None;
177 }
178
179 let void_glyph = face.as_ref().glyph;
180 let slot: FT_GlyphSlot = void_glyph;
181 assert!(!slot.is_null());
182
183 let advance = unsafe { (*slot).metrics.horiAdvance };
184 Some(fixed_26_dot_6_to_float(advance) * self.unscalable_font_metrics_scale())
185 }
186
187 fn metrics(&self) -> FontMetrics {
188 let face = self.face.lock();
189 let font_ref = self.table_provider_data.font_ref();
190
191 let freetype_size: &FT_SizeRec = unsafe { &*face.as_ref().size };
193 let freetype_metrics: &FT_Size_Metrics = &(freetype_size).metrics;
194
195 let mut max_advance;
196 let mut max_ascent;
197 let mut max_descent;
198 let mut line_height;
199 let mut y_scale = 0.0;
200 let mut em_height;
201 if face.scalable() {
202 y_scale = freetype_metrics.y_scale as f64 / 65535.0 / 64.0;
210
211 max_advance = (face.as_ref().max_advance_width as f64) * y_scale;
212 max_ascent = (face.as_ref().ascender as f64) * y_scale;
213 max_descent = -(face.as_ref().descender as f64) * y_scale;
214 line_height = (face.as_ref().height as f64) * y_scale;
215 em_height = (face.as_ref().units_per_EM as f64) * y_scale;
216 } else {
217 max_advance = fixed_26_dot_6_to_float(freetype_metrics.max_advance);
218 max_ascent = fixed_26_dot_6_to_float(freetype_metrics.ascender);
219 max_descent = -fixed_26_dot_6_to_float(freetype_metrics.descender);
220 line_height = fixed_26_dot_6_to_float(freetype_metrics.height);
221
222 em_height = freetype_metrics.y_ppem as f64;
223 if let Ok(head) = font_ref.clone().and_then(|font_ref| font_ref.head()) {
228 if face.color() {
232 em_height = self.requested_face_size.to_f64_px();
233 let adjust_scale = em_height / (freetype_metrics.y_ppem as f64);
234 max_advance *= adjust_scale;
235 max_descent *= adjust_scale;
236 max_ascent *= adjust_scale;
237 line_height *= adjust_scale;
238 }
239 y_scale = em_height / head.units_per_em() as f64;
240 }
241 }
242
243 let leading = line_height - (max_ascent + max_descent);
250
251 let underline_size = face.as_ref().underline_thickness as f64 * y_scale;
252 let underline_offset = face.as_ref().underline_position as f64 * y_scale + 0.5;
253
254 let mut strikeout_size = underline_size;
257 let mut strikeout_offset = em_height * 409.0 / 2048.0 + 0.5 * strikeout_size;
258
259 let mut x_height = 0.5 * em_height;
263 let mut average_advance = 0.0;
264
265 if let Ok(os2) = font_ref.and_then(|font_ref| font_ref.os2()) {
266 let y_strikeout_size = os2.y_strikeout_size();
267 let y_strikeout_position = os2.y_strikeout_position();
268 if !y_strikeout_size.is_zero() && !y_strikeout_position.is_zero() {
269 strikeout_size = y_strikeout_size as f64 * y_scale;
270 strikeout_offset = y_strikeout_position as f64 * y_scale;
271 }
272
273 let sx_height = os2.sx_height().unwrap_or(0);
274 if !sx_height.is_zero() {
275 x_height = sx_height as f64 * y_scale;
276 }
277
278 let x_average_char_width = os2.x_avg_char_width();
279 if !x_average_char_width.is_zero() {
280 average_advance = x_average_char_width as f64 * y_scale;
281 }
282 }
283
284 if average_advance.is_zero() {
285 average_advance = self
286 .glyph_index('0')
287 .and_then(|idx| self.glyph_h_advance(idx))
288 .map_or(max_advance, |advance| advance * y_scale);
289 }
290
291 let zero_horizontal_advance = self
292 .glyph_index('0')
293 .and_then(|idx| self.glyph_h_advance(idx))
294 .map(Au::from_f64_px);
295 let ic_horizontal_advance = self
296 .glyph_index('\u{6C34}')
297 .and_then(|idx| self.glyph_h_advance(idx))
298 .map(Au::from_f64_px);
299 let space_advance = self
300 .glyph_index(' ')
301 .and_then(|idx| self.glyph_h_advance(idx))
302 .unwrap_or(average_advance);
303
304 FontMetrics {
305 underline_size: Au::from_f64_px(underline_size),
306 underline_offset: Au::from_f64_px(underline_offset),
307 strikeout_size: Au::from_f64_px(strikeout_size),
308 strikeout_offset: Au::from_f64_px(strikeout_offset),
309 leading: Au::from_f64_px(leading),
310 x_height: Au::from_f64_px(x_height),
311 em_size: Au::from_f64_px(em_height),
312 ascent: Au::from_f64_px(max_ascent),
313 descent: Au::from_f64_px(max_descent),
314 max_advance: Au::from_f64_px(max_advance),
315 average_advance: Au::from_f64_px(average_advance),
316 line_gap: Au::from_f64_px(line_height),
317 zero_horizontal_advance,
318 ic_horizontal_advance,
319 space_advance: Au::from_f64_px(space_advance),
320 }
321 }
322
323 fn table_for_tag(&self, tag: Tag) -> Option<FontTable> {
324 let font_ref = self.table_provider_data.font_ref().ok()?;
325 let _table_data = font_ref.table_data(tag)?;
326 Some(FontTable {
327 data: self.table_provider_data.clone(),
328 tag,
329 })
330 }
331
332 fn typographic_bounds(&self, glyph_id: GlyphId) -> Rect<f32> {
333 let face = self.face.lock();
334
335 let load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
336 let result = unsafe { FT_Load_Glyph(face.as_ptr(), glyph_id as FT_UInt, load_flags) };
337 if 0 != result {
338 debug!("Unable to load glyph {}. reason: {:?}", glyph_id, result);
339 return Rect::default();
340 }
341
342 let metrics = unsafe { &(*face.as_ref().glyph).metrics };
343
344 Rect::new(
345 Point2D::new(
346 metrics.horiBearingX as f32,
347 (metrics.horiBearingY - metrics.height) as f32,
348 ),
349 Size2D::new(metrics.width as f32, metrics.height as f32),
350 ) * (1. / 64.)
351 }
352
353 fn webrender_font_instance_flags(&self) -> FontInstanceFlags {
354 FontInstanceFlags::EMBEDDED_BITMAPS
358 }
359
360 fn variations(&self) -> &[FontVariation] {
361 &self.variations
362 }
363}
364
365impl PlatformFont {
366 fn unscalable_font_metrics_scale(&self) -> f64 {
370 self.requested_face_size.to_f64_px() / self.actual_face_size.to_f64_px()
371 }
372}
373
374#[derive(Clone)]
375enum FreeTypeFaceTableProviderData {
376 Web(FontData),
377 Local(Arc<Mmap>, u32),
378}
379
380impl FreeTypeFaceTableProviderData {
381 fn font_ref(&self) -> Result<FontRef<'_>, ReadError> {
382 match self {
383 Self::Web(ipc_shared_memory) => FontRef::new(ipc_shared_memory.as_ref()),
384 Self::Local(mmap, index) => FontRef::from_index(mmap, *index),
385 }
386 }
387}
388
389impl std::fmt::Debug for FreeTypeFaceTableProviderData {
390 fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
391 Ok(())
392 }
393}