Add support for the 'cap' font-relative unit

This is a backport of https://phabricator.services.mozilla.com/D133101,
by Jonathan Kew.

Note that Servo isn't using font metrics yet, so the unit still won't
really work.
This commit is contained in:
Oriol Brufau 2023-05-10 02:26:00 +02:00
parent 61f872e7da
commit 6785c57c78
5 changed files with 68 additions and 8 deletions

View file

@ -12,20 +12,38 @@ use crate::Atom;
/// Represents the font metrics that style needs from a font to compute the
/// value of certain CSS units like `ex`.
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub struct FontMetrics {
/// The x-height of the font.
pub x_height: Option<Length>,
/// The zero advance. This is usually writing mode dependent
pub zero_advance_measure: Option<Length>,
/// The cap-height of the font.
pub cap_height: Option<Length>,
/// The ascent of the font (a value is always available for this).
pub ascent: Length,
}
impl Default for FontMetrics {
fn default() -> Self {
FontMetrics {
x_height: None,
zero_advance_measure: None,
cap_height: None,
ascent: Length::new(0.0),
}
}
}
/// Type of font metrics to retrieve.
#[derive(Clone, Debug, PartialEq)]
pub enum FontMetricsOrientation {
/// Get metrics for horizontal or vertical according to the Context's
/// writing mode.
MatchContext,
/// writing mode, using horizontal metrics for vertical/mixed
MatchContextPreferHorizontal,
/// Get metrics for horizontal or vertical according to the Context's
/// writing mode, using vertical metrics for vertical/mixed
MatchContextPreferVertical,
/// Force getting horizontal metrics.
Horizontal,
}

View file

@ -979,7 +979,12 @@ impl FontMetricsProvider for GeckoFontMetricsProvider {
};
let vertical_metrics = match orientation {
FontMetricsOrientation::MatchContext => wm.is_vertical() && wm.is_upright(),
FontMetricsOrientation::MatchContextPreferHorizontal => {
wm.is_vertical() && wm.is_upright()
},
FontMetricsOrientation::MatchContextPreferVertical => {
wm.is_vertical() && !wm.is_sideways()
},
FontMetricsOrientation::Horizontal => false,
};
let gecko_metrics = unsafe {
@ -999,6 +1004,12 @@ impl FontMetricsProvider for GeckoFontMetricsProvider {
} else {
None
},
cap_height: if gecko_metrics.mCapHeight.px() >= 0. {
Some(gecko_metrics.mCapHeight)
} else {
None
},
ascent: gecko_metrics.mAscent,
}
}
}

View file

@ -42,6 +42,7 @@ pub enum MinMaxOp {
pub enum SortKey {
Number,
Percentage,
Cap,
Ch,
Deg,
Em,

View file

@ -168,6 +168,7 @@ impl generic::CalcNodeLeaf for Leaf {
FontRelativeLength::Ch(..) => SortKey::Ch,
FontRelativeLength::Em(..) => SortKey::Em,
FontRelativeLength::Ex(..) => SortKey::Ex,
FontRelativeLength::Cap(..) => SortKey::Cap,
FontRelativeLength::Rem(..) => SortKey::Rem,
},
NoCalcLength::ViewportPercentage(ref vp) => match *vp {

View file

@ -57,6 +57,9 @@ pub enum FontRelativeLength {
/// A "ch" value: https://drafts.csswg.org/css-values/#ch
#[css(dimension)]
Ch(CSSFloat),
/// A "cap" value: https://drafts.csswg.org/css-values/#cap
#[css(dimension)]
Cap(CSSFloat),
/// A "rem" value: https://drafts.csswg.org/css-values/#rem
#[css(dimension)]
Rem(CSSFloat),
@ -90,6 +93,7 @@ impl FontRelativeLength {
FontRelativeLength::Em(v) |
FontRelativeLength::Ex(v) |
FontRelativeLength::Ch(v) |
FontRelativeLength::Cap(v) |
FontRelativeLength::Rem(v) => v == 0.,
}
}
@ -99,6 +103,7 @@ impl FontRelativeLength {
FontRelativeLength::Em(v) |
FontRelativeLength::Ex(v) |
FontRelativeLength::Ch(v) |
FontRelativeLength::Cap(v) |
FontRelativeLength::Rem(v) => v < 0.,
}
}
@ -114,12 +119,13 @@ impl FontRelativeLength {
(&Em(one), &Em(other)) => Em(one + other),
(&Ex(one), &Ex(other)) => Ex(one + other),
(&Ch(one), &Ch(other)) => Ch(one + other),
(&Cap(one), &Cap(other)) => Cap(one + other),
(&Rem(one), &Rem(other)) => Rem(one + other),
// See https://github.com/rust-lang/rust/issues/68867. rustc isn't
// able to figure it own on its own so we help.
_ => unsafe {
match *self {
Em(..) | Ex(..) | Ch(..) | Rem(..) => {},
Em(..) | Ex(..) | Ch(..) | Cap(..) | Rem(..) => {},
}
debug_unreachable!("Forgot to handle unit in try_sum()")
},
@ -207,8 +213,11 @@ impl FontRelativeLength {
// measure of a glyph is its advance width or height,
// whichever is in the inline axis of the element.)
//
let metrics =
query_font_metrics(context, base_size, FontMetricsOrientation::MatchContext);
let metrics = query_font_metrics(
context,
base_size,
FontMetricsOrientation::MatchContextPreferHorizontal,
);
let reference_size = metrics.zero_advance_measure.unwrap_or_else(|| {
// https://drafts.csswg.org/css-values/#ch
//
@ -229,6 +238,23 @@ impl FontRelativeLength {
});
(reference_size, length)
},
FontRelativeLength::Cap(length) => {
if context.for_non_inherited_property.is_some() {
context.rule_cache_conditions.borrow_mut().set_uncacheable();
}
context.builder.add_flags(font_metrics_flag);
let metrics =
query_font_metrics(context, base_size, FontMetricsOrientation::Horizontal);
let reference_size = metrics.cap_height.unwrap_or_else(|| {
// https://drafts.csswg.org/css-values/#cap
//
// In the cases where it is impossible or impractical to
// determine the cap-height, the fonts ascent must be used.
//
metrics.ascent
});
(reference_size, length)
},
FontRelativeLength::Rem(length) => {
// https://drafts.csswg.org/css-values/#rem:
//
@ -540,6 +566,7 @@ impl NoCalcLength {
"em" => NoCalcLength::FontRelative(FontRelativeLength::Em(value)),
"ex" => NoCalcLength::FontRelative(FontRelativeLength::Ex(value)),
"ch" => NoCalcLength::FontRelative(FontRelativeLength::Ch(value)),
"cap" => NoCalcLength::FontRelative(FontRelativeLength::Cap(value)),
"rem" => NoCalcLength::FontRelative(FontRelativeLength::Rem(value)),
// viewport percentages
"vw" if !context.in_page_rule() => {
@ -699,12 +726,13 @@ impl PartialOrd for FontRelativeLength {
(&Em(ref one), &Em(ref other)) => one.partial_cmp(other),
(&Ex(ref one), &Ex(ref other)) => one.partial_cmp(other),
(&Ch(ref one), &Ch(ref other)) => one.partial_cmp(other),
(&Cap(ref one), &Cap(ref other)) => one.partial_cmp(other),
(&Rem(ref one), &Rem(ref other)) => one.partial_cmp(other),
// See https://github.com/rust-lang/rust/issues/68867. rustc isn't
// able to figure it own on its own so we help.
_ => unsafe {
match *self {
Em(..) | Ex(..) | Ch(..) | Rem(..) => {},
Em(..) | Ex(..) | Ch(..) | Cap(..) | Rem(..) => {},
}
debug_unreachable!("Forgot an arm in partial_cmp?")
},
@ -721,6 +749,7 @@ impl Mul<CSSFloat> for FontRelativeLength {
FontRelativeLength::Em(v) => FontRelativeLength::Em(v * scalar),
FontRelativeLength::Ex(v) => FontRelativeLength::Ex(v * scalar),
FontRelativeLength::Ch(v) => FontRelativeLength::Ch(v * scalar),
FontRelativeLength::Cap(v) => FontRelativeLength::Cap(v * scalar),
FontRelativeLength::Rem(v) => FontRelativeLength::Rem(v * scalar),
}
}