LibWeb/CSS: Implement the font-kerning property

This sets whether the kerning information stored on the current font is
used.
This commit is contained in:
Tim Ledbetter 2025-06-22 18:59:42 +01:00 committed by Sam Atkins
commit 9b6da84fff
Notes: github-actions[bot] 2025-06-23 12:28:02 +00:00
17 changed files with 108 additions and 20 deletions

View file

@ -1252,6 +1252,12 @@ Variant<VerticalAlign, LengthPercentage> ComputedProperties::vertical_align() co
VERIFY_NOT_REACHED();
}
FontKerning ComputedProperties::font_kerning() const
{
auto const& value = property(PropertyID::FontKerning);
return keyword_to_font_kerning(value.to_keyword()).release_value();
}
Optional<FlyString> ComputedProperties::font_language_override() const
{
auto const& value = property(PropertyID::FontLanguageOverride);

View file

@ -146,6 +146,7 @@ public:
Optional<Gfx::FontVariantLigatures> font_variant_ligatures() const;
Optional<Gfx::FontVariantNumeric> font_variant_numeric() const;
FontVariantPosition font_variant_position() const;
FontKerning font_kerning() const;
Optional<FlyString> font_language_override() const;
Optional<HashMap<FlyString, IntegerOrCalculated>> font_feature_settings() const;
Optional<HashMap<FlyString, NumberOrCalculated>> font_variation_settings() const;

View file

@ -94,6 +94,7 @@ class InitialValues {
public:
static AspectRatio aspect_ratio() { return AspectRatio { true, {} }; }
static CSSPixels font_size() { return 16; }
static FontKerning font_kerning() { return FontKerning::Auto; }
static int font_weight() { return 400; }
static CSSPixels line_height() { return 0; }
static CSS::Float float_() { return CSS::Float::None; }
@ -577,6 +578,7 @@ public:
Optional<Gfx::FontVariantLigatures> font_variant_ligatures() const { return m_inherited.font_variant_ligatures; }
Optional<Gfx::FontVariantNumeric> font_variant_numeric() const { return m_inherited.font_variant_numeric; }
FontVariantPosition font_variant_position() const { return m_inherited.font_variant_position; }
FontKerning font_kerning() const { return m_inherited.font_kerning; }
Optional<FlyString> font_language_override() const { return m_inherited.font_language_override; }
Optional<HashMap<FlyString, IntegerOrCalculated>> font_feature_settings() const { return m_inherited.font_feature_settings; }
Optional<HashMap<FlyString, NumberOrCalculated>> font_variation_settings() const { return m_inherited.font_variation_settings; }
@ -619,6 +621,7 @@ protected:
Optional<Gfx::FontVariantLigatures> font_variant_ligatures;
Optional<Gfx::FontVariantNumeric> font_variant_numeric;
FontVariantPosition font_variant_position { FontVariantPosition::Normal };
FontKerning font_kerning { InitialValues::font_kerning() };
Optional<FlyString> font_language_override;
Optional<HashMap<FlyString, IntegerOrCalculated>> font_feature_settings;
Optional<HashMap<FlyString, NumberOrCalculated>> font_variation_settings;
@ -815,6 +818,7 @@ public:
void set_font_variant_ligatures(Optional<Gfx::FontVariantLigatures> font_variant_ligatures) { m_inherited.font_variant_ligatures = font_variant_ligatures; }
void set_font_variant_numeric(Optional<Gfx::FontVariantNumeric> font_variant_numeric) { m_inherited.font_variant_numeric = font_variant_numeric; }
void set_font_variant_position(FontVariantPosition font_variant_position) { m_inherited.font_variant_position = font_variant_position; }
void set_font_kerning(FontKerning font_kerning) { m_inherited.font_kerning = font_kerning; }
void set_font_language_override(Optional<FlyString> font_language_override) { m_inherited.font_language_override = font_language_override; }
void set_font_feature_settings(Optional<HashMap<FlyString, IntegerOrCalculated>> value) { m_inherited.font_feature_settings = move(value); }
void set_font_variation_settings(Optional<HashMap<FlyString, NumberOrCalculated>> value) { m_inherited.font_variation_settings = move(value); }

View file

@ -257,6 +257,11 @@
"fallback",
"optional"
],
"font-kerning": [
"auto",
"normal",
"none"
],
"font-style": [
"normal",
"italic",

View file

@ -2543,7 +2543,7 @@ RefPtr<CSSStyleValue const> Parser::parse_font_value(TokenStream<ComponentValue>
// Reset implicitly https://drafts.csswg.org/css-fonts/#reset-implicitly
PropertyID::FontFeatureSettings,
// FIXME: PropertyID::FontKerning,
PropertyID::FontKerning,
PropertyID::FontLanguageOverride,
// FIXME: PropertyID::FontOpticalSizing,
// FIXME: PropertyID::FontSizeAdjust,
@ -2561,7 +2561,7 @@ RefPtr<CSSStyleValue const> Parser::parse_font_value(TokenStream<ComponentValue>
// Reset implicitly
initial_value, // font-feature-settings
// FIXME: font-kerning,
property_initial_value(PropertyID::FontKerning), // font-kerning,
initial_value, // font-language-override
// FIXME: font-optical-sizing,
// FIXME: font-size-adjust,

View file

@ -1425,6 +1425,14 @@
"off"
]
},
"font-kerning": {
"animation-type": "discrete",
"inherited": true,
"initial": "auto",
"valid-types": [
"font-kerning"
]
},
"font-language-override": {
"animation-type": "discrete",
"inherited": true,

View file

@ -417,6 +417,20 @@ HashMap<StringView, u8> InlineLevelIterator::shape_features_map() const
}
}
// FIXME: vkrn should be enabled for vertical text.
switch (computed_values.font_kerning()) {
case CSS::FontKerning::Auto:
// FIXME: Use a heuristic to determine whether to enable kerning.
case CSS::FontKerning::Normal:
features.set("kern"sv, 1);
break;
case CSS::FontKerning::None:
features.set("kern"sv, 0);
break;
default:
break;
}
return features;
}

View file

@ -387,6 +387,7 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style)
computed_values.set_font_list(computed_style.computed_font_list());
computed_values.set_font_size(computed_style.property(CSS::PropertyID::FontSize).as_length().length().to_px(*this));
computed_values.set_font_weight(round_to<int>(computed_style.property(CSS::PropertyID::FontWeight).as_number().number()));
computed_values.set_font_kerning(computed_style.font_kerning());
computed_values.set_line_height(computed_style.line_height());
computed_values.set_vertical_align(computed_style.vertical_align());

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>CSS Test: font-kerning basic syntax support</title>
<link rel="author" title="Chris Lilley" href="chris@w3.org">
<style>
.test {
margin: 20px;
width: 100px;
height: 100px;
background-color: green
}
</style>
<body>
<p>Test passes if there is a green square, and no red. </p>
<section class="test">
</section>

View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>CSS Test: font-kerning basic syntax support</title>
<link rel="author" title="Chris Lilley" href="chris@w3.org">
<link rel="match" href="../../../../expected/wpt-import/css/css-fonts/font-kerning-01-ref.html">
<link rel="help" href="https://www.w3.org/TR/css-fonts-3/#font-kerning-prop">
<meta name="assert" content="">
<style>
.test {
margin: 20px;
width: 100px;
height: 100px;
background-color: red
}
@supports (font-kerning: auto) {
section.test { background-color: green; }
}
</style>
<body>
<p>Test passes if there is a green square, and no red. </p>
<section class="test">
</section>

View file

@ -17,6 +17,7 @@ All properties associated with getComputedStyle(document.body):
"fill-rule",
"font-family",
"font-feature-settings",
"font-kerning",
"font-language-override",
"font-size",
"font-style",

View file

@ -354,6 +354,8 @@ All supported properties and their default values exposed from CSSStylePropertie
'font-family': 'serif'
'fontFeatureSettings': 'normal'
'font-feature-settings': 'normal'
'fontKerning': 'auto'
'font-kerning': 'auto'
'fontLanguageOverride': 'normal'
'font-language-override': 'normal'
'fontSize': '16px'

View file

@ -1,5 +1,5 @@
font-feature-settings: normal
font-kerning: undefined
font-kerning: auto
font-language-override: normal
font-optical-sizing: undefined
font-size-adjust: undefined

View file

@ -15,6 +15,7 @@ fill-opacity: 1
fill-rule: nonzero
font-family: serif
font-feature-settings: normal
font-kerning: auto
font-language-override: normal
font-size: 16px
font-style: normal
@ -87,7 +88,7 @@ background-position-x: 0%
background-position-y: 0%
background-repeat: repeat
background-size: auto auto
block-size: 1335px
block-size: 1350px
border-block-end-color: rgb(0, 0, 0)
border-block-end-style: none
border-block-end-width: medium
@ -152,7 +153,7 @@ grid-row-start: auto
grid-template-areas: none
grid-template-columns: none
grid-template-rows: none
height: 2310px
height: 2325px
inline-size: 784px
inset-block-end: auto
inset-block-start: auto

View file

@ -1,8 +1,8 @@
Harness status: OK
Found 205 tests
Found 206 tests
201 Pass
202 Pass
4 Fail
Pass accent-color
Pass border-collapse
@ -20,6 +20,7 @@ Pass fill-opacity
Pass fill-rule
Pass font-family
Pass font-feature-settings
Pass font-kerning
Pass font-language-override
Pass font-size
Pass font-style

View file

@ -2,7 +2,7 @@ Harness status: OK
Found 3 tests
3 Fail
Fail Property font-kerning value 'auto'
Fail Property font-kerning value 'normal'
Fail Property font-kerning value 'none'
3 Pass
Pass Property font-kerning value 'auto'
Pass Property font-kerning value 'normal'
Pass Property font-kerning value 'none'

View file

@ -2,7 +2,7 @@ Harness status: OK
Found 3 tests
3 Fail
Fail e.style['font-kerning'] = "auto" should set the property value
Fail e.style['font-kerning'] = "normal" should set the property value
Fail e.style['font-kerning'] = "none" should set the property value
3 Pass
Pass e.style['font-kerning'] = "auto" should set the property value
Pass e.style['font-kerning'] = "normal" should set the property value
Pass e.style['font-kerning'] = "none" should set the property value