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_HAS_MULTIPLE_MASTERS,
13 FT_KERNING_DEFAULT, FT_LOAD_DEFAULT, FT_LOAD_NO_HINTING, FT_Load_Glyph, FT_Size_Metrics,
14 FT_SizeRec, FT_UInt, 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 synthetic_bold: bool,
60
61 table_provider_data: FreeTypeFaceTableProviderData,
63}
64
65impl PlatformFontMethods for PlatformFont {
66 fn new_from_data(
67 _font_identifier: FontIdentifier,
68 font_data: &FontData,
69 requested_size: Option<Au>,
70 variations: &[FontVariation],
71 mut synthetic_bold: bool,
72 ) -> Result<PlatformFont, &'static str> {
73 let library = FreeTypeLibraryHandle::get().lock();
74 let data: &[u8] = font_data.as_ref();
75 let face = FreeTypeFace::new_from_memory(&library, data)?;
76
77 let normalized_variations = face.set_variations_for_font(variations, &library)?;
78
79 let (requested_face_size, actual_face_size) = match requested_size {
80 Some(requested_size) => (requested_size, face.set_size(requested_size)?),
81 None => (Au::zero(), Au::zero()),
82 };
83
84 if FT_HAS_MULTIPLE_MASTERS(face.as_ptr()) {
90 synthetic_bold = false;
91 }
92
93 Ok(PlatformFont {
94 face: ReentrantMutex::new(face),
95 requested_face_size,
96 actual_face_size,
97 table_provider_data: FreeTypeFaceTableProviderData::Web(font_data.clone()),
98 variations: normalized_variations,
99 synthetic_bold,
100 })
101 }
102
103 fn new_from_local_font_identifier(
104 font_identifier: LocalFontIdentifier,
105 requested_size: Option<Au>,
106 variations: &[FontVariation],
107 mut synthetic_bold: bool,
108 ) -> Result<PlatformFont, &'static str> {
109 let library = FreeTypeLibraryHandle::get().lock();
110 let filename = CString::new(&*font_identifier.path).expect("filename contains NUL byte!");
111
112 let face = FreeTypeFace::new_from_file(
113 &library,
114 &filename,
115 font_identifier.face_index_for_freetype(),
116 )?;
117
118 let normalized_variations = face.set_variations_for_font(variations, &library)?;
119
120 let (requested_face_size, actual_face_size) = match requested_size {
121 Some(requested_size) => (requested_size, face.set_size(requested_size)?),
122 None => (Au::zero(), Au::zero()),
123 };
124
125 let Ok(memory_mapped_font_data) =
126 File::open(&*font_identifier.path).and_then(|file| unsafe { Mmap::map(&file) })
127 else {
128 return Err("Could not memory map");
129 };
130
131 if FT_HAS_MULTIPLE_MASTERS(face.as_ptr()) {
137 synthetic_bold = false;
138 }
139
140 Ok(PlatformFont {
141 face: ReentrantMutex::new(face),
142 requested_face_size,
143 actual_face_size,
144 table_provider_data: FreeTypeFaceTableProviderData::Local(
145 Arc::new(memory_mapped_font_data),
146 font_identifier.index(),
147 ),
148 variations: normalized_variations,
149 synthetic_bold,
150 })
151 }
152
153 fn descriptor(&self) -> FontTemplateDescriptor {
154 let Ok(font_ref) = self.table_provider_data.font_ref() else {
155 return FontTemplateDescriptor::default();
156 };
157 let Ok(os2) = font_ref.os2() else {
158 return FontTemplateDescriptor::default();
159 };
160 Self::descriptor_from_os2_table(&os2)
161 }
162
163 fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
164 let face = self.face.lock();
165
166 unsafe {
167 let idx = FT_Get_Char_Index(face.as_ptr(), codepoint as FT_ULong);
168 if idx != 0 as FT_UInt {
169 Some(idx as GlyphId)
170 } else {
171 debug!(
172 "Invalid codepoint: U+{:04X} ('{}')",
173 codepoint as u32, codepoint
174 );
175 None
176 }
177 }
178 }
179
180 fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId) -> FractionalPixel {
181 let face = self.face.lock();
182
183 let mut delta = FT_Vector { x: 0, y: 0 };
184 unsafe {
185 FT_Get_Kerning(
186 face.as_ptr(),
187 first_glyph,
188 second_glyph,
189 FT_KERNING_DEFAULT,
190 &mut delta,
191 );
192 }
193 fixed_26_dot_6_to_float(delta.x) * self.unscalable_font_metrics_scale()
194 }
195
196 fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
197 let face = self.face.lock();
198
199 let load_flags = face.glyph_load_flags();
200 let result = unsafe { FT_Load_Glyph(face.as_ptr(), glyph as FT_UInt, load_flags) };
201 if 0 != result {
202 debug!("Unable to load glyph {}. reason: {:?}", glyph, result);
203 return None;
204 }
205
206 let void_glyph = face.as_ref().glyph;
207 let slot: FT_GlyphSlot = void_glyph;
208 assert!(!slot.is_null());
209
210 if self.synthetic_bold {
211 mozilla_glyphslot_embolden_less(slot);
212 }
213
214 let advance = unsafe { (*slot).metrics.horiAdvance };
215 Some(fixed_26_dot_6_to_float(advance) * self.unscalable_font_metrics_scale())
216 }
217
218 fn metrics(&self) -> FontMetrics {
219 let face = self.face.lock();
220 let font_ref = self.table_provider_data.font_ref();
221
222 let freetype_size: &FT_SizeRec = unsafe { &*face.as_ref().size };
224 let freetype_metrics: &FT_Size_Metrics = &(freetype_size).metrics;
225
226 let mut max_advance;
227 let mut max_ascent;
228 let mut max_descent;
229 let mut line_height;
230 let mut y_scale = 0.0;
231 let mut em_height;
232 if face.scalable() {
233 y_scale = freetype_metrics.y_scale as f64 / 65535.0 / 64.0;
241
242 max_advance = (face.as_ref().max_advance_width as f64) * y_scale;
243 max_ascent = (face.as_ref().ascender as f64) * y_scale;
244 max_descent = -(face.as_ref().descender as f64) * y_scale;
245 line_height = (face.as_ref().height as f64) * y_scale;
246 em_height = (face.as_ref().units_per_EM as f64) * y_scale;
247 } else {
248 max_advance = fixed_26_dot_6_to_float(freetype_metrics.max_advance);
249 max_ascent = fixed_26_dot_6_to_float(freetype_metrics.ascender);
250 max_descent = -fixed_26_dot_6_to_float(freetype_metrics.descender);
251 line_height = fixed_26_dot_6_to_float(freetype_metrics.height);
252
253 em_height = freetype_metrics.y_ppem as f64;
254 if let Ok(head) = font_ref.clone().and_then(|font_ref| font_ref.head()) {
259 if face.color() {
263 em_height = self.requested_face_size.to_f64_px();
264 let adjust_scale = em_height / (freetype_metrics.y_ppem as f64);
265 max_advance *= adjust_scale;
266 max_descent *= adjust_scale;
267 max_ascent *= adjust_scale;
268 line_height *= adjust_scale;
269 }
270 y_scale = em_height / head.units_per_em() as f64;
271 }
272 }
273
274 let leading = line_height - (max_ascent + max_descent);
281
282 let underline_size = face.as_ref().underline_thickness as f64 * y_scale;
283 let underline_offset = face.as_ref().underline_position as f64 * y_scale + 0.5;
284
285 let mut strikeout_size = underline_size;
288 let mut strikeout_offset = em_height * 409.0 / 2048.0 + 0.5 * strikeout_size;
289
290 let mut x_height = 0.5 * em_height;
294 let mut average_advance = 0.0;
295
296 if let Ok(os2) = font_ref.and_then(|font_ref| font_ref.os2()) {
297 let y_strikeout_size = os2.y_strikeout_size();
298 let y_strikeout_position = os2.y_strikeout_position();
299 if !y_strikeout_size.is_zero() && !y_strikeout_position.is_zero() {
300 strikeout_size = y_strikeout_size as f64 * y_scale;
301 strikeout_offset = y_strikeout_position as f64 * y_scale;
302 }
303
304 let sx_height = os2.sx_height().unwrap_or(0);
305 if !sx_height.is_zero() {
306 x_height = sx_height as f64 * y_scale;
307 }
308
309 let x_average_char_width = os2.x_avg_char_width();
310 if !x_average_char_width.is_zero() {
311 average_advance = x_average_char_width as f64 * y_scale;
312 }
313 }
314
315 if average_advance.is_zero() {
316 average_advance = self
317 .glyph_index('0')
318 .and_then(|idx| self.glyph_h_advance(idx))
319 .map_or(max_advance, |advance| advance * y_scale);
320 }
321
322 let zero_horizontal_advance = self
323 .glyph_index('0')
324 .and_then(|idx| self.glyph_h_advance(idx))
325 .map(Au::from_f64_px);
326 let ic_horizontal_advance = self
327 .glyph_index('\u{6C34}')
328 .and_then(|idx| self.glyph_h_advance(idx))
329 .map(Au::from_f64_px);
330 let space_advance = self
331 .glyph_index(' ')
332 .and_then(|idx| self.glyph_h_advance(idx))
333 .unwrap_or(average_advance);
334
335 FontMetrics {
336 underline_size: Au::from_f64_px(underline_size),
337 underline_offset: Au::from_f64_px(underline_offset),
338 strikeout_size: Au::from_f64_px(strikeout_size),
339 strikeout_offset: Au::from_f64_px(strikeout_offset),
340 leading: Au::from_f64_px(leading),
341 x_height: Au::from_f64_px(x_height),
342 em_size: Au::from_f64_px(em_height),
343 ascent: Au::from_f64_px(max_ascent),
344 descent: Au::from_f64_px(max_descent),
345 max_advance: Au::from_f64_px(max_advance),
346 average_advance: Au::from_f64_px(average_advance),
347 line_gap: Au::from_f64_px(line_height),
348 zero_horizontal_advance,
349 ic_horizontal_advance,
350 space_advance: Au::from_f64_px(space_advance),
351 }
352 }
353
354 fn table_for_tag(&self, tag: Tag) -> Option<FontTable> {
355 let font_ref = self.table_provider_data.font_ref().ok()?;
356 let _table_data = font_ref.table_data(tag)?;
357 Some(FontTable {
358 data: self.table_provider_data.clone(),
359 tag,
360 })
361 }
362
363 fn typographic_bounds(&self, glyph_id: GlyphId) -> Rect<f32> {
364 let face = self.face.lock();
365
366 let load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
367 let result = unsafe { FT_Load_Glyph(face.as_ptr(), glyph_id as FT_UInt, load_flags) };
368 if 0 != result {
369 debug!("Unable to load glyph {}. reason: {:?}", glyph_id, result);
370 return Rect::default();
371 }
372
373 let metrics = unsafe { &(*face.as_ref().glyph).metrics };
374
375 Rect::new(
376 Point2D::new(
377 metrics.horiBearingX as f32,
378 (metrics.horiBearingY - metrics.height) as f32,
379 ),
380 Size2D::new(metrics.width as f32, metrics.height as f32),
381 ) * (1. / 64.)
382 }
383
384 fn webrender_font_instance_flags(&self) -> FontInstanceFlags {
385 let mut flags = FontInstanceFlags::EMBEDDED_BITMAPS;
389
390 if self.synthetic_bold {
391 flags |= FontInstanceFlags::SYNTHETIC_BOLD;
392 }
393
394 flags
395 }
396
397 fn variations(&self) -> &[FontVariation] {
398 &self.variations
399 }
400}
401
402impl PlatformFont {
403 fn unscalable_font_metrics_scale(&self) -> f64 {
407 self.requested_face_size.to_f64_px() / self.actual_face_size.to_f64_px()
408 }
409}
410
411#[derive(Clone)]
412enum FreeTypeFaceTableProviderData {
413 Web(FontData),
414 Local(Arc<Mmap>, u32),
415}
416
417impl FreeTypeFaceTableProviderData {
418 fn font_ref(&self) -> Result<FontRef<'_>, ReadError> {
419 match self {
420 Self::Web(ipc_shared_memory) => FontRef::new(ipc_shared_memory.as_ref()),
421 Self::Local(mmap, index) => FontRef::from_index(mmap, *index),
422 }
423 }
424}
425
426impl std::fmt::Debug for FreeTypeFaceTableProviderData {
427 fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
428 Ok(())
429 }
430}
431
432fn mozilla_glyphslot_embolden_less(slot: FT_GlyphSlot) {
438 use freetype_sys::{
439 FT_GLYPH_FORMAT_OUTLINE, FT_GlyphSlot_Embolden, FT_Long, FT_MulFix, FT_Outline_Embolden,
440 };
441
442 if slot.is_null() {
443 return;
444 }
445
446 let slot_ = unsafe { &mut *slot };
447 let format = slot_.format;
448 if format != FT_GLYPH_FORMAT_OUTLINE {
449 unsafe { FT_GlyphSlot_Embolden(slot) };
451 return;
452 }
453
454 let face_ = unsafe { &*slot_.face };
455
456 let size_ = unsafe { &*face_.size };
459 let strength = unsafe { FT_MulFix(face_.units_per_EM as FT_Long, size_.metrics.y_scale) / 48 };
460 unsafe { FT_Outline_Embolden(&raw mut slot_.outline, strength) };
461
462 if slot_.advance.x != 0 {
464 slot_.advance.x += strength;
465 }
466 if slot_.advance.y != 0 {
467 slot_.advance.y += strength;
468 }
469 slot_.metrics.width += strength;
470 slot_.metrics.height += strength;
471 slot_.metrics.horiAdvance += strength;
472 slot_.metrics.vertAdvance += strength;
473 slot_.metrics.horiBearingY += strength;
474}