1use std::fs::File;
6
7use app_units::Au;
8use euclid::default::{Point2D, Rect, Size2D};
9use fonts_traits::{FontIdentifier, FontTemplateDescriptor, LocalFontIdentifier};
10use freetype_sys::{
11 FT_F26Dot6, FT_Get_Char_Index, FT_Get_Kerning, FT_GlyphSlot, FT_KERNING_DEFAULT,
12 FT_LOAD_DEFAULT, FT_LOAD_NO_HINTING, FT_Load_Glyph, FT_Size_Metrics, FT_SizeRec, FT_UInt,
13 FT_ULong, FT_Vector,
14};
15use log::debug;
16use memmap2::Mmap;
17use parking_lot::ReentrantMutex;
18use read_fonts::types::Tag;
19use read_fonts::{FontRef, ReadError, TableProvider};
20use servo_arc::Arc;
21use skrifa::attribute::Weight;
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::{FontBackingStore, FreeTypeFace};
30
31const SEMI_BOLD_U16: u16 = Weight::SEMI_BOLD.value() as u16;
32
33fn fixed_26_dot_6_to_float(fixed: FT_F26Dot6) -> f64 {
35 fixed as f64 / 64.0
36}
37
38#[derive(Debug)]
39pub struct FontTable {
40 data: FreeTypeFaceTableProviderData,
41 tag: Tag,
42}
43
44impl FontTableMethods for FontTable {
45 fn buffer(&self) -> &[u8] {
46 let font_ref = self.data.font_ref().expect("Font checked before creating");
47 let table_data = font_ref
48 .table_data(self.tag)
49 .expect("Table existence checked before creating");
50 table_data.as_bytes()
51 }
52}
53
54#[derive(Debug)]
55pub struct PlatformFont {
56 face: ReentrantMutex<FreeTypeFace>,
57 requested_face_size: Au,
58 actual_face_size: Au,
59 variations: Vec<FontVariation>,
60 synthetic_bold: bool,
61
62 table_provider_data: FreeTypeFaceTableProviderData,
64}
65
66impl PlatformFontMethods for PlatformFont {
67 fn new_from_data(
68 _font_identifier: FontIdentifier,
69 font_data: &FontData,
70 requested_size: Option<Au>,
71 synthetic_bold: bool,
72 ) -> Result<PlatformFont, &'static str> {
73 let library = FreeTypeLibraryHandle::get().lock();
74 let data = FontBackingStore::Web(font_data.clone());
75 let face = FreeTypeFace::new_from_memory(&library, data, 0)?;
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 let table_provider_data = FreeTypeFaceTableProviderData::Web(font_data.clone());
83
84 let synthetic_bold = table_provider_data.should_apply_synthetic_bold(synthetic_bold);
85
86 Ok(PlatformFont {
87 face: ReentrantMutex::new(face),
88 requested_face_size,
89 actual_face_size,
90 table_provider_data,
91 variations: vec![],
92 synthetic_bold,
93 })
94 }
95
96 fn new_from_local_font_identifier(
97 font_identifier: LocalFontIdentifier,
98 requested_size: Option<Au>,
99 synthetic_bold: bool,
100 ) -> Result<PlatformFont, &'static str> {
101 let library = FreeTypeLibraryHandle::get().lock();
102
103 let Ok(memory_mapped_font_data) = File::open(&*font_identifier.path)
104 .and_then(|file| unsafe { Mmap::map(&file) })
105 .map(Arc::new)
106 else {
107 return Err("Could not memory map font");
108 };
109
110 let face_index = font_identifier.face_index_for_freetype();
111 let face = FreeTypeFace::new_from_memory(
112 &library,
113 FontBackingStore::Local(memory_mapped_font_data.clone()),
114 face_index,
115 )?;
116
117 let (requested_face_size, actual_face_size) = match requested_size {
118 Some(requested_size) => (requested_size, face.set_size(requested_size)?),
119 None => (Au::zero(), Au::zero()),
120 };
121
122 let table_provider_data =
123 FreeTypeFaceTableProviderData::Local(memory_mapped_font_data, font_identifier.index());
124
125 let synthetic_bold = table_provider_data.should_apply_synthetic_bold(synthetic_bold);
126
127 Ok(PlatformFont {
128 face: ReentrantMutex::new(face),
129 requested_face_size,
130 actual_face_size,
131 table_provider_data,
132 variations: vec![],
133 synthetic_bold,
134 })
135 }
136
137 fn copy_with_variations(
138 mut self,
139 _: &FontIdentifier,
140 variations: &[FontVariation],
141 ) -> Result<Self, &'static str> {
142 let library = FreeTypeLibraryHandle::get().lock();
143 self.variations = self
144 .face
145 .lock()
146 .set_variations_for_font(variations, &library)?;
147 Ok(self)
148 }
149
150 fn descriptor(&self) -> FontTemplateDescriptor {
151 let Ok(font_ref) = self.table_provider_data.font_ref() else {
152 return FontTemplateDescriptor::default();
153 };
154 let Ok(os2) = font_ref.os2() else {
155 return FontTemplateDescriptor::default();
156 };
157 Self::descriptor_from_os2_table(&os2)
158 }
159
160 fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
161 let face = self.face.lock();
162
163 unsafe {
164 let idx = FT_Get_Char_Index(face.as_ptr(), codepoint as FT_ULong);
165 if idx != 0 as FT_UInt {
166 Some(idx as GlyphId)
167 } else {
168 debug!(
169 "Invalid codepoint: U+{:04X} ('{}')",
170 codepoint as u32, codepoint
171 );
172 None
173 }
174 }
175 }
176
177 fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId) -> FractionalPixel {
178 let face = self.face.lock();
179
180 let mut delta = FT_Vector { x: 0, y: 0 };
181 unsafe {
182 FT_Get_Kerning(
183 face.as_ptr(),
184 first_glyph,
185 second_glyph,
186 FT_KERNING_DEFAULT,
187 &mut delta,
188 );
189 }
190 fixed_26_dot_6_to_float(delta.x) * self.unscalable_font_metrics_scale()
191 }
192
193 fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
194 let face = self.face.lock();
195
196 let load_flags = face.glyph_load_flags();
197 let result = unsafe { FT_Load_Glyph(face.as_ptr(), glyph as FT_UInt, load_flags) };
198 if 0 != result {
199 debug!("Unable to load glyph {}. reason: {:?}", glyph, result);
200 return None;
201 }
202
203 let void_glyph = face.as_ref().glyph;
204 let slot: FT_GlyphSlot = void_glyph;
205 if void_glyph.is_null() {
206 return None;
207 }
208
209 if self.synthetic_bold {
210 mozilla_glyphslot_embolden_less(slot);
211 }
212
213 let advance = unsafe { (*slot).metrics.horiAdvance };
214 Some(fixed_26_dot_6_to_float(advance) * self.unscalable_font_metrics_scale())
215 }
216
217 fn metrics(&self) -> FontMetrics {
218 let face = self.face.lock();
219 let font_ref = self.table_provider_data.font_ref();
220
221 let freetype_size: &FT_SizeRec = unsafe { &*face.as_ref().size };
223 let freetype_metrics: &FT_Size_Metrics = &(freetype_size).metrics;
224
225 let mut max_advance;
226 let mut max_ascent;
227 let mut max_descent;
228 let mut line_height;
229 let mut y_scale = 0.0;
230 let mut em_height;
231 if face.scalable() {
232 y_scale = freetype_metrics.y_scale as f64 / 65535.0 / 64.0;
240
241 max_advance = (face.as_ref().max_advance_width as f64) * y_scale;
242 max_ascent = (face.as_ref().ascender as f64) * y_scale;
243 max_descent = -(face.as_ref().descender as f64) * y_scale;
244 line_height = (face.as_ref().height as f64) * y_scale;
245 em_height = (face.as_ref().units_per_EM as f64) * y_scale;
246 } else {
247 max_advance = fixed_26_dot_6_to_float(freetype_metrics.max_advance);
248 max_ascent = fixed_26_dot_6_to_float(freetype_metrics.ascender);
249 max_descent = -fixed_26_dot_6_to_float(freetype_metrics.descender);
250 line_height = fixed_26_dot_6_to_float(freetype_metrics.height);
251
252 em_height = freetype_metrics.y_ppem as f64;
253 if let Ok(head) = font_ref.clone().and_then(|font_ref| font_ref.head()) {
258 if face.color() {
262 em_height = self.requested_face_size.to_f64_px();
263 let adjust_scale = em_height / (freetype_metrics.y_ppem as f64);
264 max_advance *= adjust_scale;
265 max_descent *= adjust_scale;
266 max_ascent *= adjust_scale;
267 line_height *= adjust_scale;
268 }
269 y_scale = em_height / head.units_per_em() as f64;
270 }
271 }
272
273 let leading = line_height - (max_ascent + max_descent);
280
281 let underline_size = face.as_ref().underline_thickness as f64 * y_scale;
282 let underline_offset = face.as_ref().underline_position as f64 * y_scale + 0.5;
283
284 let mut strikeout_size = underline_size;
287 let mut strikeout_offset = em_height * 409.0 / 2048.0 + 0.5 * strikeout_size;
288
289 let mut x_height = 0.5 * em_height;
293 let mut average_advance = 0.0;
294
295 if let Ok(os2) = font_ref.and_then(|font_ref| font_ref.os2()) {
296 let y_strikeout_size = os2.y_strikeout_size();
297 let y_strikeout_position = os2.y_strikeout_position();
298 if !y_strikeout_size.is_zero() && !y_strikeout_position.is_zero() {
299 strikeout_size = y_strikeout_size as f64 * y_scale;
300 strikeout_offset = y_strikeout_position as f64 * y_scale;
301 }
302
303 let sx_height = os2.sx_height().unwrap_or(0);
304 if !sx_height.is_zero() {
305 x_height = sx_height as f64 * y_scale;
306 }
307
308 let x_average_char_width = os2.x_avg_char_width();
309 if !x_average_char_width.is_zero() {
310 average_advance = x_average_char_width as f64 * y_scale;
311 }
312 }
313
314 if average_advance.is_zero() {
315 average_advance = self
316 .glyph_index('0')
317 .and_then(|idx| self.glyph_h_advance(idx))
318 .map_or(max_advance, |advance| advance * y_scale);
319 }
320
321 let zero_horizontal_advance = self
322 .glyph_index('0')
323 .and_then(|idx| self.glyph_h_advance(idx))
324 .map(Au::from_f64_px);
325 let ic_horizontal_advance = self
326 .glyph_index('\u{6C34}')
327 .and_then(|idx| self.glyph_h_advance(idx))
328 .map(Au::from_f64_px);
329 let space_advance = self
330 .glyph_index(' ')
331 .and_then(|idx| self.glyph_h_advance(idx))
332 .unwrap_or(average_advance);
333
334 FontMetrics {
335 underline_size: Au::from_f64_px(underline_size),
336 underline_offset: Au::from_f64_px(underline_offset),
337 strikeout_size: Au::from_f64_px(strikeout_size),
338 strikeout_offset: Au::from_f64_px(strikeout_offset),
339 leading: Au::from_f64_px(leading),
340 x_height: Au::from_f64_px(x_height),
341 em_size: Au::from_f64_px(em_height),
342 ascent: Au::from_f64_px(max_ascent),
343 descent: Au::from_f64_px(max_descent),
344 max_advance: Au::from_f64_px(max_advance),
345 average_advance: Au::from_f64_px(average_advance),
346 line_gap: Au::from_f64_px(line_height),
347 zero_horizontal_advance,
348 ic_horizontal_advance,
349 space_advance: Au::from_f64_px(space_advance),
350 }
351 }
352
353 fn table_for_tag(&self, tag: Tag) -> Option<FontTable> {
354 let font_ref = self.table_provider_data.font_ref().ok()?;
355 let _table_data = font_ref.table_data(tag)?;
356 Some(FontTable {
357 data: self.table_provider_data.clone(),
358 tag,
359 })
360 }
361
362 fn typographic_bounds(&self, glyph_id: GlyphId) -> Rect<f32> {
363 let face = self.face.lock();
364
365 let load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
366 let result = unsafe { FT_Load_Glyph(face.as_ptr(), glyph_id as FT_UInt, load_flags) };
367 if 0 != result {
368 debug!("Unable to load glyph {}. reason: {:?}", glyph_id, result);
369 return Rect::default();
370 }
371
372 let metrics = unsafe { &(*face.as_ref().glyph).metrics };
373
374 Rect::new(
375 Point2D::new(
376 metrics.horiBearingX as f32,
377 (metrics.horiBearingY - metrics.height) as f32,
378 ),
379 Size2D::new(metrics.width as f32, metrics.height as f32),
380 ) * (1. / 64.)
381 }
382
383 fn webrender_font_instance_flags(&self) -> FontInstanceFlags {
384 let mut flags = FontInstanceFlags::EMBEDDED_BITMAPS;
388
389 if self.synthetic_bold {
392 flags |= FontInstanceFlags::SYNTHETIC_BOLD;
393 }
394
395 flags
396 }
397
398 fn variations(&self) -> &[FontVariation] {
399 &self.variations
400 }
401}
402
403impl PlatformFont {
404 fn unscalable_font_metrics_scale(&self) -> f64 {
408 self.requested_face_size.to_f64_px() / self.actual_face_size.to_f64_px()
409 }
410}
411
412#[derive(Clone)]
413enum FreeTypeFaceTableProviderData {
414 Web(FontData),
415 Local(Arc<Mmap>, u32),
416}
417
418impl FreeTypeFaceTableProviderData {
419 fn font_ref(&self) -> Result<FontRef<'_>, ReadError> {
420 match self {
421 Self::Web(ipc_shared_memory) => FontRef::new(ipc_shared_memory.as_ref()),
422 Self::Local(mmap, index) => FontRef::from_index(mmap, *index),
423 }
424 }
425
426 fn should_apply_synthetic_bold(&self, synthetic_bold: bool) -> bool {
427 let face_is_bold = self
430 .font_ref()
431 .and_then(|font_ref| font_ref.os2())
432 .is_ok_and(|table| table.us_weight_class() >= SEMI_BOLD_U16);
433 let is_variable_font = self
434 .font_ref()
435 .and_then(|font_ref| font_ref.fvar())
436 .is_ok_and(|table| table.axis_count() > 0);
437 !face_is_bold && !is_variable_font && synthetic_bold
438 }
439}
440
441impl std::fmt::Debug for FreeTypeFaceTableProviderData {
442 fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
443 Ok(())
444 }
445}
446
447fn mozilla_glyphslot_embolden_less(slot: FT_GlyphSlot) {
453 use freetype_sys::{
454 FT_GLYPH_FORMAT_OUTLINE, FT_GlyphSlot_Embolden, FT_Long, FT_MulFix, FT_Outline_Embolden,
455 };
456
457 if slot.is_null() {
458 return;
459 }
460
461 let slot_ = unsafe { &mut *slot };
462 let format = slot_.format;
463 if format != FT_GLYPH_FORMAT_OUTLINE {
464 unsafe { FT_GlyphSlot_Embolden(slot) };
466 return;
467 }
468
469 let face_ = unsafe { &*slot_.face };
470
471 let size_ = unsafe { &*face_.size };
474 let strength = unsafe { FT_MulFix(face_.units_per_EM as FT_Long, size_.metrics.y_scale) / 48 };
475 unsafe { FT_Outline_Embolden(&raw mut slot_.outline, strength) };
476
477 if slot_.advance.x != 0 {
479 slot_.advance.x += strength;
480 }
481 if slot_.advance.y != 0 {
482 slot_.advance.y += strength;
483 }
484 slot_.metrics.width += strength;
485 slot_.metrics.height += strength;
486 slot_.metrics.horiAdvance += strength;
487 slot_.metrics.vertAdvance += strength;
488 slot_.metrics.horiBearingY += strength;
489}