aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml1
-rw-r--r--components/gfx/Cargo.toml1
-rw-r--r--components/gfx/platform/freetype/font.rs405
-rw-r--r--components/gfx/platform/freetype/font_list.rs8
-rw-r--r--tests/wpt/meta-legacy-layout/css/css-fonts/font-size-zero-1.html.ini3
-rw-r--r--tests/wpt/meta-legacy-layout/css/css-text-decor/text-decoration-thickness-from-zero-sized-font.html.ini2
-rw-r--r--tests/wpt/meta/css/css-text/text-transform/text-transform-capitalize-026.html.ini2
8 files changed, 299 insertions, 130 deletions
diff --git a/Cargo.lock b/Cargo.lock
index c747b85dd2a..e909acbbe0e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2017,6 +2017,7 @@ dependencies = [
"truetype",
"ucd",
"unicode-bidi",
+ "unicode-properties",
"unicode-script",
"webrender_api",
"xi-unicode",
@@ -6634,6 +6635,12 @@ dependencies = [
]
[[package]]
+name = "unicode-properties"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291"
+
+[[package]]
name = "unicode-script"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index d8b1190f94f..0ea83dc0b7a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -116,6 +116,7 @@ tokio-rustls = "0.24"
tungstenite = "0.20"
uluru = "3.0"
unicode-bidi = "0.3.15"
+unicode-properties = { version = "0.1.1", features = ["emoji"] }
unicode-script = "0.5"
unicode-segmentation = "1.1.0"
url = "2.5"
diff --git a/components/gfx/Cargo.toml b/components/gfx/Cargo.toml
index f7153a155e3..dd4ad758a6e 100644
--- a/components/gfx/Cargo.toml
+++ b/components/gfx/Cargo.toml
@@ -41,6 +41,7 @@ surfman = { workspace = true }
style = { workspace = true }
ucd = "0.1.1"
unicode-bidi = { workspace = true, features = ["with_serde"] }
+unicode-properties = { workspace = true }
unicode-script = { workspace = true }
webrender_api = { workspace = true }
xi-unicode = { workspace = true }
diff --git a/components/gfx/platform/freetype/font.rs b/components/gfx/platform/freetype/font.rs
index c6c7f802c3b..9a913e17f96 100644
--- a/components/gfx/platform/freetype/font.rs
+++ b/components/gfx/platform/freetype/font.rs
@@ -9,10 +9,12 @@ use std::{mem, ptr};
use app_units::Au;
use freetype::freetype::{
- FT_Done_Face, FT_F26Dot6, FT_Face, FT_Get_Char_Index, FT_Get_Kerning, FT_Get_Sfnt_Table,
- FT_GlyphSlot, FT_Int32, FT_Kerning_Mode, FT_Load_Glyph, FT_Load_Sfnt_Table, FT_Long,
- FT_New_Memory_Face, FT_Set_Char_Size, FT_Sfnt_Tag, FT_SizeRec, FT_Size_Metrics, FT_UInt,
- FT_ULong, FT_Vector, FT_STYLE_FLAG_ITALIC,
+ FT_Done_Face, FT_F26Dot6, FT_Face, FT_Fixed, FT_Get_Char_Index, FT_Get_Kerning,
+ FT_Get_Sfnt_Table, FT_GlyphSlot, FT_Int32, FT_Kerning_Mode, FT_Load_Glyph, FT_Load_Sfnt_Table,
+ FT_Long, FT_MulFix, FT_New_Memory_Face, FT_Select_Size, FT_Set_Char_Size, FT_Sfnt_Tag,
+ FT_Short, FT_SizeRec, FT_Size_Metrics, FT_UInt, FT_ULong, FT_UShort, FT_Vector,
+ FT_FACE_FLAG_COLOR, FT_FACE_FLAG_FIXED_SIZES, FT_FACE_FLAG_SCALABLE, FT_LOAD_COLOR,
+ FT_LOAD_DEFAULT, FT_STYLE_FLAG_ITALIC,
};
use freetype::succeeded;
use freetype::tt_os2::TT_OS2;
@@ -21,6 +23,7 @@ use parking_lot::ReentrantMutex;
use style::computed_values::font_stretch::T as FontStretch;
use style::computed_values::font_weight::T as FontWeight;
use style::values::computed::font::FontStyle;
+use style::Zero;
use webrender_api::FontInstanceFlags;
use super::library_handle::FreeTypeLibraryHandle;
@@ -31,21 +34,15 @@ use crate::font::{
use crate::font_cache_thread::FontIdentifier;
use crate::font_template::FontTemplateDescriptor;
use crate::text::glyph::GlyphId;
-use crate::text::util::fixed_to_float;
// This constant is not present in the freetype
// bindings due to bindgen not handling the way
// the macro is defined.
-const FT_LOAD_TARGET_LIGHT: FT_Int32 = 1 << 16;
+const FT_LOAD_TARGET_LIGHT: FT_UInt = 1 << 16;
-// Default to slight hinting, which is what most
-// Linux distros use by default, and is a better
-// default than no hinting.
-// TODO(gw): Make this configurable.
-const GLYPH_LOAD_FLAGS: FT_Int32 = FT_LOAD_TARGET_LIGHT;
-
-fn fixed_to_float_ft(f: i32) -> f64 {
- fixed_to_float(6, f)
+/// Convert FreeType-style 26.6 fixed point to an [`f64`].
+fn fixed_26_dot_6_to_float(fixed: FT_F26Dot6) -> f64 {
+ fixed as f64 / 64.0
}
#[derive(Debug)]
@@ -63,11 +60,12 @@ impl FontTableMethods for FontTable {
/// See <https://www.microsoft.com/typography/otspec/os2.htm>
#[derive(Debug)]
struct OS2Table {
- us_weight_class: u16,
- us_width_class: u16,
- y_strikeout_size: i16,
- y_strikeout_position: i16,
- sx_height: i16,
+ x_average_char_width: FT_Short,
+ us_weight_class: FT_UShort,
+ us_width_class: FT_UShort,
+ y_strikeout_size: FT_Short,
+ y_strikeout_position: FT_Short,
+ sx_height: FT_Short,
}
#[derive(Debug)]
@@ -77,6 +75,8 @@ pub struct PlatformFont {
/// platform [`FT_Face`].
font_data: Arc<Vec<u8>>,
face: ReentrantMutex<FT_Face>,
+ requested_face_size: Au,
+ actual_face_size: Au,
can_do_fast_shaping: bool,
}
@@ -102,11 +102,7 @@ impl Drop for PlatformFont {
}
}
-fn create_face(
- data: Arc<Vec<u8>>,
- face_index: u32,
- pt_size: Option<Au>,
-) -> Result<FT_Face, &'static str> {
+fn create_face(data: Arc<Vec<u8>>, face_index: u32) -> Result<FT_Face, &'static str> {
unsafe {
let mut face: FT_Face = ptr::null_mut();
let library = FreeTypeLibraryHandle::get().lock();
@@ -125,10 +121,6 @@ fn create_face(
return Err("Could not create FreeType face");
}
- if let Some(s) = pt_size {
- PlatformFont::set_char_size(face, s)?
- }
-
Ok(face)
}
}
@@ -138,20 +130,24 @@ impl PlatformFontMethods for PlatformFont {
_font_identifier: FontIdentifier,
data: Arc<Vec<u8>>,
face_index: u32,
- pt_size: Option<Au>,
+ requested_size: Option<Au>,
) -> Result<PlatformFont, &'static str> {
- let face = create_face(data.clone(), face_index, pt_size)?;
- let mut handle = PlatformFont {
- face: ReentrantMutex::new(face),
- font_data: data,
- can_do_fast_shaping: false,
- };
+ let face = create_face(data.clone(), face_index)?;
- // TODO (#11310): Implement basic support for GPOS and GSUB.
- handle.can_do_fast_shaping =
- handle.has_table(KERN) && !handle.has_table(GPOS) && !handle.has_table(GSUB);
+ let (requested_face_size, actual_face_size) = match requested_size {
+ Some(requested_size) => (requested_size, face.set_size(requested_size)?),
+ None => (Au::zero(), Au::zero()),
+ };
+ let can_do_fast_shaping =
+ face.has_table(KERN) && !face.has_table(GPOS) && !face.has_table(GSUB);
- Ok(handle)
+ Ok(PlatformFont {
+ face: ReentrantMutex::new(face),
+ font_data: data,
+ requested_face_size,
+ actual_face_size,
+ can_do_fast_shaping,
+ })
}
fn descriptor(&self) -> FontTemplateDescriptor {
@@ -162,7 +158,8 @@ impl PlatformFontMethods for PlatformFont {
FontStyle::NORMAL
};
- let os2_table = self.os2_table();
+ let face = self.face.lock();
+ let os2_table = face.os2_table();
let weight = os2_table
.as_ref()
.map(|os2| FontWeight::from_float(os2.us_weight_class as f32))
@@ -218,7 +215,7 @@ impl PlatformFontMethods for PlatformFont {
&mut delta,
);
}
- fixed_to_float_ft(delta.x as i32)
+ fixed_26_dot_6_to_float(delta.x) * self.unscalable_font_metrics_scale()
}
fn can_do_fast_shaping(&self) -> bool {
@@ -229,51 +226,125 @@ impl PlatformFontMethods for PlatformFont {
let face = self.face.lock();
assert!(!face.is_null());
- unsafe {
- let res = FT_Load_Glyph(*face, glyph as FT_UInt, GLYPH_LOAD_FLAGS);
- if succeeded(res) {
- let void_glyph = (**face).glyph;
- let slot: FT_GlyphSlot = void_glyph;
- assert!(!slot.is_null());
- let advance = (*slot).metrics.horiAdvance;
- debug!("h_advance for {} is {}", glyph, advance);
- let advance = advance as i32;
- Some(fixed_to_float_ft(advance) as FractionalPixel)
- } else {
- debug!("Unable to load glyph {}. reason: {:?}", glyph, res);
- None
- }
+ let load_flags = face.glyph_load_flags();
+ let result = unsafe { FT_Load_Glyph(*face, glyph as FT_UInt, load_flags) };
+ if !succeeded(result) {
+ debug!("Unable to load glyph {}. reason: {:?}", glyph, result);
+ return None;
}
+
+ let void_glyph = unsafe { (**face).glyph };
+ let slot: FT_GlyphSlot = void_glyph;
+ assert!(!slot.is_null());
+
+ let advance = unsafe { (*slot).metrics.horiAdvance };
+ Some(fixed_26_dot_6_to_float(advance) * self.unscalable_font_metrics_scale())
}
fn metrics(&self) -> FontMetrics {
- let face = self.face.lock();
- let face = unsafe { **face };
-
- let underline_size = self.font_units_to_au(face.underline_thickness as f64);
- let underline_offset = self.font_units_to_au(face.underline_position as f64);
- let em_size = self.font_units_to_au(face.units_per_EM as f64);
- let ascent = self.font_units_to_au(face.ascender as f64);
- let descent = self.font_units_to_au(face.descender as f64);
- let max_advance = self.font_units_to_au(face.max_advance_width as f64);
+ let face_ptr = *self.face.lock();
+ let face = unsafe { *face_ptr };
+
+ // face.size is a *c_void in the bindings, presumably to avoid recursive structural types
+ let freetype_size: &FT_SizeRec = unsafe { mem::transmute(&(*face.size)) };
+ let freetype_metrics: &FT_Size_Metrics = &(freetype_size).metrics;
+
+ let mut max_advance;
+ let mut max_ascent;
+ let mut max_descent;
+ let mut line_height;
+ let mut y_scale = 0.0;
+ let mut em_height;
+ if face_ptr.scalable() {
+ // Prefer FT_Size_Metrics::y_scale to y_ppem as y_ppem does not have subpixel accuracy.
+ //
+ // FT_Size_Metrics::y_scale is in 16.16 fixed point format. Its (fractional) value is a
+ // factor that converts vertical metrics from design units to units of 1/64 pixels, so
+ // that the result may be interpreted as pixels in 26.6 fixed point format.
+ //
+ // This converts the value to a float without losing precision.
+ y_scale = freetype_metrics.y_scale as f64 / 65535.0 / 64.0;
+
+ max_advance = (face.max_advance_width as f64) * y_scale;
+ max_ascent = (face.ascender as f64) * y_scale;
+ max_descent = -(face.descender as f64) * y_scale;
+ line_height = (face.height as f64) * y_scale;
+ em_height = (face.units_per_EM as f64) * y_scale;
+ } else {
+ max_advance = fixed_26_dot_6_to_float(freetype_metrics.max_advance);
+ max_ascent = fixed_26_dot_6_to_float(freetype_metrics.ascender);
+ max_descent = -fixed_26_dot_6_to_float(freetype_metrics.descender);
+ line_height = fixed_26_dot_6_to_float(freetype_metrics.height);
+
+ em_height = freetype_metrics.y_ppem as f64;
+ // FT_Face doc says units_per_EM and a bunch of following fields are "only relevant to
+ // scalable outlines". If it's an sfnt, we can get units_per_EM from the 'head' table
+ // instead; otherwise, we don't have a unitsPerEm value so we can't compute y_scale and
+ // x_scale.
+ let head =
+ unsafe { FT_Get_Sfnt_Table(face_ptr, FT_Sfnt_Tag::FT_SFNT_HEAD) as *mut TtHeader };
+ if !head.is_null() && unsafe { (*head).table_version != 0xffff } {
+ // Bug 1267909 - Even if the font is not explicitly scalable, if the face has color
+ // bitmaps, it should be treated as scalable and scaled to the desired size. Metrics
+ // based on y_ppem need to be rescaled for the adjusted size.
+ if face_ptr.color() {
+ em_height = self.requested_face_size.to_f64_px();
+ let adjust_scale = em_height / (freetype_metrics.y_ppem as f64);
+ max_advance *= adjust_scale;
+ max_descent *= adjust_scale;
+ max_ascent *= adjust_scale;
+ line_height *= adjust_scale;
+ }
+ let units_per_em = unsafe { (*head).units_per_em } as f64;
+ y_scale = em_height / units_per_em;
+ }
+ }
// 'leading' is supposed to be the vertical distance between two baselines,
- // reflected by the height attribute in freetype. On OS X (w/ CTFont),
+ // reflected by the height attribute in freetype. On OS X (w/ CTFont),
// leading represents the distance between the bottom of a line descent to
// the top of the next line's ascent or: (line_height - ascent - descent),
// see http://stackoverflow.com/a/5635981 for CTFont implementation.
// Convert using a formula similar to what CTFont returns for consistency.
- let height = self.font_units_to_au(face.height as f64);
- let leading = height - (ascent + descent);
+ let leading = line_height - (max_ascent + max_descent);
+
+ let underline_size = face.underline_thickness as f64 * y_scale;
+ let underline_offset = face.underline_position as f64 * y_scale + 0.5;
+
+ // The default values for strikeout size and offset. Use OpenType spec's suggested position
+ // for Roman font as the default for offset.
+ let mut strikeout_size = underline_size;
+ let mut strikeout_offset = em_height * 409.0 / 2048.0 + 0.5 * strikeout_size;
+
+ // CSS 2.1, section 4.3.2 Lengths: "In the cases where it is
+ // impossible or impractical to determine the x-height, a value of
+ // 0.5em should be used."
+ let mut x_height = 0.5 * em_height;
+ let mut average_advance = 0.0;
+ if let Some(os2) = face_ptr.os2_table() {
+ if !os2.y_strikeout_size.is_zero() && !os2.y_strikeout_position.is_zero() {
+ strikeout_size = os2.y_strikeout_size as f64 * y_scale;
+ strikeout_offset = os2.y_strikeout_position as f64 * y_scale;
+ }
+ if !os2.sx_height.is_zero() {
+ x_height = os2.sx_height as f64 * y_scale;
+ }
- let mut strikeout_size = Au(0);
- let mut strikeout_offset = Au(0);
- let mut x_height = Au(0);
+ if !os2.x_average_char_width.is_zero() {
+ average_advance = fixed_26_dot_6_to_float(unsafe {
+ FT_MulFix(
+ os2.x_average_char_width as FT_F26Dot6,
+ freetype_metrics.x_scale,
+ )
+ });
+ }
+ }
- if let Some(os2) = self.os2_table() {
- strikeout_size = self.font_units_to_au(os2.y_strikeout_size as f64);
- strikeout_offset = self.font_units_to_au(os2.y_strikeout_position as f64);
- x_height = self.font_units_to_au(os2.sx_height as f64);
+ if average_advance.is_zero() {
+ average_advance = self
+ .glyph_index('0')
+ .and_then(|idx| self.glyph_h_advance(idx))
+ .map_or(max_advance, |advance| advance * y_scale);
}
let zero_horizontal_advance = self
@@ -285,27 +356,22 @@ impl PlatformFontMethods for PlatformFont {
.and_then(|idx| self.glyph_h_advance(idx))
.map(Au::from_f64_px);
- let average_advance = zero_horizontal_advance.unwrap_or(max_advance);
-
- let metrics = FontMetrics {
- underline_size,
- underline_offset,
- strikeout_size,
- strikeout_offset,
- leading,
- x_height,
- em_size,
- ascent,
- descent: -descent, // linux font's seem to use the opposite sign from mac
- max_advance,
- average_advance,
- line_gap: height,
+ FontMetrics {
+ underline_size: Au::from_f64_px(underline_size),
+ underline_offset: Au::from_f64_px(underline_offset),
+ strikeout_size: Au::from_f64_px(strikeout_size),
+ strikeout_offset: Au::from_f64_px(strikeout_offset),
+ leading: Au::from_f64_px(leading),
+ x_height: Au::from_f64_px(x_height),
+ em_size: Au::from_f64_px(em_height),
+ ascent: Au::from_f64_px(max_ascent),
+ descent: Au::from_f64_px(max_descent),
+ max_advance: Au::from_f64_px(max_advance),
+ average_advance: Au::from_f64_px(average_advance),
+ line_gap: Au::from_f64_px(line_height),
zero_horizontal_advance,
ic_horizontal_advance,
- };
-
- debug!("Font metrics (@{}px): {:?}", em_size.to_f32_px(), metrics);
- metrics
+ }
}
fn table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
@@ -334,29 +400,106 @@ impl PlatformFontMethods for PlatformFont {
}
fn webrender_font_instance_flags(&self) -> FontInstanceFlags {
- FontInstanceFlags::empty()
+ // On other platforms, we only pass this when we know that we are loading a font with
+ // color characters, but not passing this flag simply *prevents* WebRender from
+ // loading bitmaps. There's no harm to always passing it.
+ FontInstanceFlags::EMBEDDED_BITMAPS
}
}
impl PlatformFont {
- fn set_char_size(face: FT_Face, pt_size: Au) -> Result<(), &'static str> {
- let char_size = pt_size.to_f64_px() * 64.0 + 0.5;
+ /// Find the scale to use for metrics of unscalable fonts. Unscalable fonts, those using bitmap
+ /// glyphs, are scaled after glyph rasterization. In order for metrics to match the final scaled
+ /// font, we need to scale them based on the final size and the actual font size.
+ fn unscalable_font_metrics_scale(&self) -> f64 {
+ self.requested_face_size.to_f64_px() / self.actual_face_size.to_f64_px()
+ }
+}
- unsafe {
- let result = FT_Set_Char_Size(face, char_size as FT_F26Dot6, 0, 0, 0);
- if succeeded(result) {
- Ok(())
- } else {
- Err("FT_Set_Char_Size failed")
+trait FreeTypeFaceHelpers {
+ fn scalable(self) -> bool;
+ fn color(self) -> bool;
+ fn set_size(self, pt_size: Au) -> Result<Au, &'static str>;
+ fn glyph_load_flags(self) -> FT_Int32;
+ fn has_table(self, tag: FontTableTag) -> bool;
+ fn os2_table(self) -> Option<OS2Table>;
+}
+
+impl FreeTypeFaceHelpers for FT_Face {
+ fn scalable(self) -> bool {
+ unsafe { (*self).face_flags & FT_FACE_FLAG_SCALABLE as c_long != 0 }
+ }
+
+ fn color(self) -> bool {
+ unsafe { (*self).face_flags & FT_FACE_FLAG_COLOR as c_long != 0 }
+ }
+
+ fn set_size(self, requested_size: Au) -> Result<Au, &'static str> {
+ if self.scalable() {
+ let size_in_fixed_point = (requested_size.to_f64_px() * 64.0 + 0.5) as FT_F26Dot6;
+ let result = unsafe { FT_Set_Char_Size(self, size_in_fixed_point, 0, 72, 72) };
+ if !succeeded(result) {
+ return Err("FT_Set_Char_Size failed");
}
+ return Ok(requested_size);
+ }
+
+ let requested_size = (requested_size.to_f64_px() * 64.0) as i64;
+ let get_size_at_index = |index| unsafe {
+ (
+ (*(*self).available_sizes.offset(index as isize)).x_ppem as i64,
+ (*(*self).available_sizes.offset(index as isize)).y_ppem as i64,
+ )
+ };
+
+ let mut best_index = 0;
+ let mut best_size = get_size_at_index(0);
+ let mut best_dist = best_size.1 - requested_size;
+ for strike_index in 1..unsafe { (*self).num_fixed_sizes } {
+ let new_scale = get_size_at_index(strike_index);
+ let new_distance = new_scale.1 - requested_size;
+
+ // Distance is positive if strike is larger than desired size,
+ // or negative if smaller. If previously a found smaller strike,
+ // then prefer a larger strike. Otherwise, minimize distance.
+ if (best_dist < 0 && new_distance >= best_dist) || new_distance.abs() <= best_dist {
+ best_dist = new_distance;
+ best_size = new_scale;
+ best_index = strike_index;
+ }
+ }
+
+ if succeeded(unsafe { FT_Select_Size(self, best_index) }) {
+ return Ok(Au::from_f64_px(best_size.1 as f64 / 64.0));
+ } else {
+ Err("FT_Select_Size failed")
}
}
- fn has_table(&self, tag: FontTableTag) -> bool {
- let face = self.face.lock();
+ fn glyph_load_flags(self) -> FT_Int32 {
+ let mut load_flags = FT_LOAD_DEFAULT;
+
+ // Default to slight hinting, which is what most
+ // Linux distros use by default, and is a better
+ // default than no hinting.
+ // TODO(gw): Make this configurable.
+ load_flags |= FT_LOAD_TARGET_LIGHT;
+
+ let face_flags = unsafe { (*self).face_flags };
+ if (face_flags & (FT_FACE_FLAG_FIXED_SIZES as FT_Long)) != 0 {
+ // We only set FT_LOAD_COLOR if there are bitmap strikes; COLR (color-layer) fonts
+ // will be handled internally in Servo. In that case WebRender will just be asked to
+ // paint individual layers.
+ load_flags |= FT_LOAD_COLOR;
+ }
+
+ load_flags as FT_Int32
+ }
+
+ fn has_table(self, tag: FontTableTag) -> bool {
unsafe {
succeeded(FT_Load_Sfnt_Table(
- *face,
+ self,
tag as FT_ULong,
0,
ptr::null_mut(),
@@ -365,29 +508,9 @@ impl PlatformFont {
}
}
- fn font_units_to_au(&self, value: f64) -> Au {
- let face = self.face.lock();
- let face = unsafe { **face };
-
- // face.size is a *c_void in the bindings, presumably to avoid
- // recursive structural types
- let size: &FT_SizeRec = unsafe { mem::transmute(&(*face.size)) };
- let metrics: &FT_Size_Metrics = &(size).metrics;
-
- let em_size = face.units_per_EM as f64;
- let x_scale = (metrics.x_ppem as f64) / em_size;
-
- // If this isn't true then we're scaling one of the axes wrong
- assert_eq!(metrics.x_ppem, metrics.y_ppem);
-
- Au::from_f64_px(value * x_scale)
- }
-
- fn os2_table(&self) -> Option<OS2Table> {
- let face = self.face.lock();
-
+ fn os2_table(self) -> Option<OS2Table> {
unsafe {
- let os2 = FT_Get_Sfnt_Table(*face, FT_Sfnt_Tag::FT_SFNT_OS2) as *mut TT_OS2;
+ let os2 = FT_Get_Sfnt_Table(self, FT_Sfnt_Tag::FT_SFNT_OS2) as *mut TT_OS2;
let valid = !os2.is_null() && (*os2).version != 0xffff;
if !valid {
@@ -395,6 +518,7 @@ impl PlatformFont {
}
Some(OS2Table {
+ x_average_char_width: (*os2).xAvgCharWidth,
us_weight_class: (*os2).usWeightClass,
us_width_class: (*os2).usWidthClass,
y_strikeout_size: (*os2).yStrikeoutSize,
@@ -404,3 +528,30 @@ impl PlatformFont {
}
}
}
+
+#[repr(C)]
+struct TtHeader {
+ table_version: FT_Fixed,
+ font_revision: FT_Fixed,
+
+ checksum_adjust: FT_Long,
+ magic_number: FT_Long,
+
+ flags: FT_UShort,
+ units_per_em: FT_UShort,
+
+ created: [FT_ULong; 2],
+ modified: [FT_ULong; 2],
+
+ x_min: FT_Short,
+ y_min: FT_Short,
+ x_max: FT_Short,
+ y_max: FT_Short,
+
+ mac_style: FT_UShort,
+ lowest_rec_ppem: FT_UShort,
+
+ font_direction: FT_Short,
+ index_to_loc_format: FT_Short,
+ glyph_data_format: FT_Short,
+}
diff --git a/components/gfx/platform/freetype/font_list.rs b/components/gfx/platform/freetype/font_list.rs
index 1462b37ca56..00db8e6855e 100644
--- a/components/gfx/platform/freetype/font_list.rs
+++ b/components/gfx/platform/freetype/font_list.rs
@@ -28,6 +28,7 @@ use malloc_size_of_derive::MallocSizeOf;
use serde::{Deserialize, Serialize};
use style::values::computed::{FontStretch, FontStyle, FontWeight};
use style::Atom;
+use unicode_properties::UnicodeEmoji;
use super::c_str_to_string;
use crate::font::map_platform_values_to_style_values;
@@ -197,7 +198,12 @@ pub static SANS_SERIF_FONT_FAMILY: &str = "DejaVu Sans";
// Based on gfxPlatformGtk::GetCommonFallbackFonts() in Gecko
pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
- let mut families = vec!["DejaVu Serif", "FreeSerif", "DejaVu Sans", "FreeSans"];
+ let mut families = Vec::new();
+
+ if codepoint.map_or(false, |codepoint| codepoint.is_emoji_char()) {
+ families.push("Noto Color Emoji");
+ }
+ families.extend(["DejaVu Serif", "FreeSerif", "DejaVu Sans", "FreeSans"]);
if let Some(codepoint) = codepoint {
if is_cjk(codepoint) {
diff --git a/tests/wpt/meta-legacy-layout/css/css-fonts/font-size-zero-1.html.ini b/tests/wpt/meta-legacy-layout/css/css-fonts/font-size-zero-1.html.ini
new file mode 100644
index 00000000000..042929a3e9b
--- /dev/null
+++ b/tests/wpt/meta-legacy-layout/css/css-fonts/font-size-zero-1.html.ini
@@ -0,0 +1,3 @@
+[font-size-zero-1.html]
+ expected: FAIL
+
diff --git a/tests/wpt/meta-legacy-layout/css/css-text-decor/text-decoration-thickness-from-zero-sized-font.html.ini b/tests/wpt/meta-legacy-layout/css/css-text-decor/text-decoration-thickness-from-zero-sized-font.html.ini
deleted file mode 100644
index ba4be2d12c7..00000000000
--- a/tests/wpt/meta-legacy-layout/css/css-text-decor/text-decoration-thickness-from-zero-sized-font.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[text-decoration-thickness-from-zero-sized-font.html]
- expected: FAIL
diff --git a/tests/wpt/meta/css/css-text/text-transform/text-transform-capitalize-026.html.ini b/tests/wpt/meta/css/css-text/text-transform/text-transform-capitalize-026.html.ini
new file mode 100644
index 00000000000..6e52a555638
--- /dev/null
+++ b/tests/wpt/meta/css/css-text/text-transform/text-transform-capitalize-026.html.ini
@@ -0,0 +1,2 @@
+[text-transform-capitalize-026.html]
+ expected: FAIL