LibWeb: Compute font features in ComputedProperties

By doing this in computed properties rather than InlineLevelIterator we
only do it once per element rather than once per text fragment.

Reduces runtime of this process from ~15% to ~0.2% when loading
https://en.wikipedia.org/wiki/2023_in_American_television
This commit is contained in:
Callum Law 2025-11-09 21:00:22 +13:00 committed by Andreas Kling
parent 7a5b948b5b
commit bbb344d534
Notes: github-actions[bot] 2025-11-10 11:13:01 +00:00
6 changed files with 278 additions and 325 deletions

View file

@ -1389,6 +1389,277 @@ Optional<FlyString> ComputedProperties::font_language_override() const
return {};
}
Gfx::ShapeFeatures ComputedProperties::font_features() const
{
HashMap<StringView, u8> merged_features;
auto font_variant_features = [&]() {
HashMap<StringView, u8> features;
// 6.4 https://drafts.csswg.org/css-fonts/#font-variant-ligatures-prop
auto ligature_or_null = font_variant_ligatures();
auto disable_all_ligatures = [&]() {
features.set("liga"sv, 0);
features.set("clig"sv, 0);
features.set("dlig"sv, 0);
features.set("hlig"sv, 0);
features.set("calt"sv, 0);
};
if (ligature_or_null.has_value()) {
auto ligature = ligature_or_null.release_value();
if (ligature.none) {
// Specifies that all types of ligatures and contextual forms covered by this property are explicitly disabled.
disable_all_ligatures();
} else {
switch (ligature.common) {
case Gfx::FontVariantLigatures::Common::Common:
// Enables display of common ligatures (OpenType features: liga, clig).
features.set("liga"sv, 1);
features.set("clig"sv, 1);
break;
case Gfx::FontVariantLigatures::Common::NoCommon:
// Disables display of common ligatures (OpenType features: liga, clig).
features.set("liga"sv, 0);
features.set("clig"sv, 0);
break;
case Gfx::FontVariantLigatures::Common::Unset:
break;
}
switch (ligature.discretionary) {
case Gfx::FontVariantLigatures::Discretionary::Discretionary:
// Enables display of discretionary ligatures (OpenType feature: dlig).
features.set("dlig"sv, 1);
break;
case Gfx::FontVariantLigatures::Discretionary::NoDiscretionary:
// Disables display of discretionary ligatures (OpenType feature: dlig).
features.set("dlig"sv, 0);
break;
case Gfx::FontVariantLigatures::Discretionary::Unset:
break;
}
switch (ligature.historical) {
case Gfx::FontVariantLigatures::Historical::Historical:
// Enables display of historical ligatures (OpenType feature: hlig).
features.set("hlig"sv, 1);
break;
case Gfx::FontVariantLigatures::Historical::NoHistorical:
// Disables display of historical ligatures (OpenType feature: hlig).
features.set("hlig"sv, 0);
break;
case Gfx::FontVariantLigatures::Historical::Unset:
break;
}
switch (ligature.contextual) {
case Gfx::FontVariantLigatures::Contextual::Contextual:
// Enables display of contextual ligatures (OpenType feature: calt).
features.set("calt"sv, 1);
break;
case Gfx::FontVariantLigatures::Contextual::NoContextual:
// Disables display of contextual ligatures (OpenType feature: calt).
features.set("calt"sv, 0);
break;
case Gfx::FontVariantLigatures::Contextual::Unset:
break;
}
}
} else if (text_rendering() == CSS::TextRendering::Optimizespeed) {
// AD-HOC: Disable ligatures if font-variant-ligatures is set to normal and text rendering is set to optimize speed.
disable_all_ligatures();
} else {
// A value of normal specifies that common default features are enabled, as described in detail in the next section.
features.set("liga"sv, 1);
features.set("clig"sv, 1);
}
// 6.5 https://drafts.csswg.org/css-fonts/#font-variant-position-prop
switch (font_variant_position()) {
case CSS::FontVariantPosition::Normal:
// None of the features listed below are enabled.
break;
case CSS::FontVariantPosition::Sub:
// Enables display of subscripts (OpenType feature: subs).
features.set("subs"sv, 1);
break;
case CSS::FontVariantPosition::Super:
// Enables display of superscripts (OpenType feature: sups).
features.set("sups"sv, 1);
break;
default:
break;
}
// 6.6 https://drafts.csswg.org/css-fonts/#font-variant-caps-prop
switch (font_variant_caps()) {
case CSS::FontVariantCaps::Normal:
// None of the features listed below are enabled.
break;
case CSS::FontVariantCaps::SmallCaps:
// Enables display of small capitals (OpenType feature: smcp). Small-caps glyphs typically use the form of uppercase letters but are reduced to the size of lowercase letters.
features.set("smcp"sv, 1);
break;
case CSS::FontVariantCaps::AllSmallCaps:
// Enables display of small capitals for both upper and lowercase letters (OpenType features: c2sc, smcp).
features.set("c2sc"sv, 1);
features.set("smcp"sv, 1);
break;
case CSS::FontVariantCaps::PetiteCaps:
// Enables display of petite capitals (OpenType feature: pcap).
features.set("pcap"sv, 1);
break;
case CSS::FontVariantCaps::AllPetiteCaps:
// Enables display of petite capitals for both upper and lowercase letters (OpenType features: c2pc, pcap).
features.set("c2pc"sv, 1);
features.set("pcap"sv, 1);
break;
case CSS::FontVariantCaps::Unicase:
// Enables display of mixture of small capitals for uppercase letters with normal lowercase letters (OpenType feature: unic).
features.set("unic"sv, 1);
break;
case CSS::FontVariantCaps::TitlingCaps:
// Enables display of titling capitals (OpenType feature: titl).
features.set("titl"sv, 1);
break;
default:
break;
}
// 6.7 https://drafts.csswg.org/css-fonts/#font-variant-numeric-prop
auto numeric_or_null = font_variant_numeric();
if (numeric_or_null.has_value()) {
auto numeric = numeric_or_null.release_value();
if (numeric.figure == Gfx::FontVariantNumeric::Figure::Oldstyle) {
// Enables display of old-style numerals (OpenType feature: onum).
features.set("onum"sv, 1);
} else if (numeric.figure == Gfx::FontVariantNumeric::Figure::Lining) {
// Enables display of lining numerals (OpenType feature: lnum).
features.set("lnum"sv, 1);
}
if (numeric.spacing == Gfx::FontVariantNumeric::Spacing::Proportional) {
// Enables display of proportional numerals (OpenType feature: pnum).
features.set("pnum"sv, 1);
} else if (numeric.spacing == Gfx::FontVariantNumeric::Spacing::Tabular) {
// Enables display of tabular numerals (OpenType feature: tnum).
features.set("tnum"sv, 1);
}
if (numeric.fraction == Gfx::FontVariantNumeric::Fraction::Diagonal) {
// Enables display of diagonal fractions (OpenType feature: frac).
features.set("frac"sv, 1);
} else if (numeric.fraction == Gfx::FontVariantNumeric::Fraction::Stacked) {
// Enables display of stacked fractions (OpenType feature: afrc).
features.set("afrc"sv, 1);
features.set("afrc"sv, 1);
}
if (numeric.ordinal) {
// Enables display of letter forms used with ordinal numbers (OpenType feature: ordn).
features.set("ordn"sv, 1);
}
if (numeric.slashed_zero) {
// Enables display of slashed zeros (OpenType feature: zero).
features.set("zero"sv, 1);
}
}
// 6.10 https://drafts.csswg.org/css-fonts/#font-variant-east-asian-prop
auto east_asian_or_null = font_variant_east_asian();
if (east_asian_or_null.has_value()) {
auto east_asian = east_asian_or_null.release_value();
switch (east_asian.variant) {
case Gfx::FontVariantEastAsian::Variant::Jis78:
// Enables display of JIS78 forms (OpenType feature: jp78).
features.set("jp78"sv, 1);
break;
case Gfx::FontVariantEastAsian::Variant::Jis83:
// Enables display of JIS83 forms (OpenType feature: jp83).
features.set("jp83"sv, 1);
break;
case Gfx::FontVariantEastAsian::Variant::Jis90:
// Enables display of JIS90 forms (OpenType feature: jp90).
features.set("jp90"sv, 1);
break;
case Gfx::FontVariantEastAsian::Variant::Jis04:
// Enables display of JIS04 forms (OpenType feature: jp04).
features.set("jp04"sv, 1);
break;
case Gfx::FontVariantEastAsian::Variant::Simplified:
// Enables display of simplified forms (OpenType feature: smpl).
features.set("smpl"sv, 1);
break;
case Gfx::FontVariantEastAsian::Variant::Traditional:
// Enables display of traditional forms (OpenType feature: trad).
features.set("trad"sv, 1);
break;
default:
break;
}
switch (east_asian.width) {
case Gfx::FontVariantEastAsian::Width::FullWidth:
// Enables display of full-width forms (OpenType feature: fwid).
features.set("fwid"sv, 1);
break;
case Gfx::FontVariantEastAsian::Width::Proportional:
// Enables display of proportional-width forms (OpenType feature: pwid).
features.set("pwid"sv, 1);
break;
default:
break;
}
if (east_asian.ruby) {
// Enables display of ruby forms (OpenType feature: ruby).
features.set("ruby"sv, 1);
}
}
// FIXME: vkrn should be enabled for vertical text.
switch (font_kerning()) {
case CSS::FontKerning::Auto:
// AD-HOC: Disable kerning if font-kerning is set to normal and text rendering is set to optimize speed.
features.set("kern"sv, text_rendering() != CSS::TextRendering::Optimizespeed ? 1 : 0);
break;
case CSS::FontKerning::Normal:
features.set("kern"sv, 1);
break;
case CSS::FontKerning::None:
features.set("kern"sv, 0);
break;
default:
break;
}
return features;
};
// https://www.w3.org/TR/css-fonts-3/#feature-precedence
// FIXME 1. Font features enabled by default, including features required for a given script.
// FIXME 2. If the font is defined via an @font-face rule, the font features implied by the font-feature-settings descriptor in the @font-face rule.
// 3. Font features implied by the value of the font-variant property, the related font-variant subproperties and any other CSS property that uses OpenType features (e.g. the font-kerning property).
merged_features.update(font_variant_features());
// FIXME 4. Feature settings determined by properties other than font-variant or font-feature-settings. For example, setting a non-default value for the letter-spacing property disables common ligatures.
// 5. Font features implied by the value of font-feature-settings property.
merged_features.update(font_feature_settings());
Gfx::ShapeFeatures shape_features;
shape_features.ensure_capacity(merged_features.size());
for (auto& it : merged_features) {
shape_features.unchecked_append({ { it.key[0], it.key[1], it.key[2], it.key[3] }, static_cast<u32>(it.value) });
}
return shape_features;
}
Optional<Gfx::FontVariantAlternates> ComputedProperties::font_variant_alternates() const
{
auto const& value = property(PropertyID::FontVariantAlternates);

View file

@ -150,6 +150,7 @@ public:
BoxSizing box_sizing() const;
PointerEvents pointer_events() const;
Variant<VerticalAlign, LengthPercentage> vertical_align() const;
Gfx::ShapeFeatures font_features() const;
Optional<Gfx::FontVariantAlternates> font_variant_alternates() const;
FontVariantCaps font_variant_caps() const;
Optional<Gfx::FontVariantEastAsian> font_variant_east_asian() const;

View file

@ -134,8 +134,8 @@ class InitialValues {
public:
static AspectRatio aspect_ratio() { return AspectRatio { true, {} }; }
static CSSPixels font_size() { return 16; }
static FontKerning font_kerning() { return FontKerning::Auto; }
static double font_weight() { return 400; }
static Gfx::ShapeFeatures font_features() { return {}; }
static CSSPixels line_height() { return 0; }
static Float float_() { return Float::None; }
static Length border_spacing() { return Length::make_px(0); }
@ -161,7 +161,6 @@ public:
static TextOverflow text_overflow() { return TextOverflow::Clip; }
static LengthPercentage text_indent() { return Length::make_px(0); }
static TextWrapMode text_wrap_mode() { return TextWrapMode::Wrap; }
static TextRendering text_rendering() { return TextRendering::Auto; }
static CSSPixels text_underline_offset() { return 2; }
static TextUnderlinePosition text_underline_position() { return { .horizontal = TextUnderlinePositionHorizontal::Auto, .vertical = TextUnderlinePositionVertical::Auto }; }
static Display display() { return Display { DisplayOutside::Inline, DisplayInside::Flow }; }
@ -500,7 +499,6 @@ public:
TextJustify text_justify() const { return m_inherited.text_justify; }
LengthPercentage const& text_indent() const { return m_inherited.text_indent; }
TextWrapMode text_wrap_mode() const { return m_inherited.text_wrap_mode; }
TextRendering text_rendering() const { return m_inherited.text_rendering; }
CSSPixels text_underline_offset() const { return m_inherited.text_underline_offset; }
TextUnderlinePosition text_underline_position() const { return m_inherited.text_underline_position; }
Vector<TextDecorationLine> const& text_decoration_line() const { return m_noninherited.text_decoration_line; }
@ -643,16 +641,8 @@ public:
Gfx::FontCascadeList const& font_list() const { return *m_inherited.font_list; }
CSSPixels font_size() const { return m_inherited.font_size; }
double font_weight() const { return m_inherited.font_weight; }
Optional<Gfx::FontVariantAlternates> font_variant_alternates() const { return m_inherited.font_variant_alternates; }
FontVariantCaps font_variant_caps() const { return m_inherited.font_variant_caps; }
Optional<Gfx::FontVariantEastAsian> font_variant_east_asian() const { return m_inherited.font_variant_east_asian; }
FontVariantEmoji font_variant_emoji() const { return m_inherited.font_variant_emoji; }
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; }
Gfx::ShapeFeatures font_features() const { return m_inherited.font_features; }
Optional<FlyString> font_language_override() const { return m_inherited.font_language_override; }
HashMap<StringView, u8> font_feature_settings() const { return m_inherited.font_feature_settings; }
Optional<HashMap<FlyString, NumberOrCalculated>> font_variation_settings() const { return m_inherited.font_variation_settings; }
CSSPixels line_height() const { return m_inherited.line_height; }
Time transition_delay() const { return m_noninherited.transition_delay; }
@ -688,16 +678,8 @@ protected:
RefPtr<Gfx::FontCascadeList const> font_list {};
CSSPixels font_size { InitialValues::font_size() };
double font_weight { InitialValues::font_weight() };
Optional<Gfx::FontVariantAlternates> font_variant_alternates;
FontVariantCaps font_variant_caps { FontVariantCaps::Normal };
Optional<Gfx::FontVariantEastAsian> font_variant_east_asian;
FontVariantEmoji font_variant_emoji { FontVariantEmoji::Normal };
Optional<Gfx::FontVariantLigatures> font_variant_ligatures;
Optional<Gfx::FontVariantNumeric> font_variant_numeric;
FontVariantPosition font_variant_position { FontVariantPosition::Normal };
FontKerning font_kerning { InitialValues::font_kerning() };
Gfx::ShapeFeatures font_features { InitialValues::font_features() };
Optional<FlyString> font_language_override;
HashMap<StringView, u8> font_feature_settings;
Optional<HashMap<FlyString, NumberOrCalculated>> font_variation_settings;
CSSPixels line_height { InitialValues::line_height() };
BorderCollapse border_collapse { InitialValues::border_collapse() };
@ -720,7 +702,6 @@ protected:
TextTransform text_transform { InitialValues::text_transform() };
LengthPercentage text_indent { InitialValues::text_indent() };
TextWrapMode text_wrap_mode { InitialValues::text_wrap_mode() };
TextRendering text_rendering { InitialValues::text_rendering() };
CSSPixels text_underline_offset { InitialValues::text_underline_offset() };
TextUnderlinePosition text_underline_position { InitialValues::text_underline_position() };
WhiteSpaceCollapse white_space_collapse { InitialValues::white_space_collapse() };
@ -900,16 +881,8 @@ public:
void set_font_list(NonnullRefPtr<Gfx::FontCascadeList const> font_list) { m_inherited.font_list = move(font_list); }
void set_font_size(CSSPixels font_size) { m_inherited.font_size = font_size; }
void set_font_weight(double font_weight) { m_inherited.font_weight = font_weight; }
void set_font_variant_alternates(Optional<Gfx::FontVariantAlternates> font_variant_alternates) { m_inherited.font_variant_alternates = move(font_variant_alternates); }
void set_font_variant_caps(FontVariantCaps font_variant_caps) { m_inherited.font_variant_caps = font_variant_caps; }
void set_font_variant_east_asian(Optional<Gfx::FontVariantEastAsian> font_variant_east_asian) { m_inherited.font_variant_east_asian = move(font_variant_east_asian); }
void set_font_variant_emoji(FontVariantEmoji font_variant_emoji) { m_inherited.font_variant_emoji = font_variant_emoji; }
void set_font_variant_ligatures(Optional<Gfx::FontVariantLigatures> font_variant_ligatures) { m_inherited.font_variant_ligatures = move(font_variant_ligatures); }
void set_font_variant_numeric(Optional<Gfx::FontVariantNumeric> font_variant_numeric) { m_inherited.font_variant_numeric = move(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_features(Gfx::ShapeFeatures font_features) { m_inherited.font_features = move(font_features); }
void set_font_language_override(Optional<FlyString> font_language_override) { m_inherited.font_language_override = move(font_language_override); }
void set_font_feature_settings(HashMap<StringView, u8> 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); }
void set_line_height(CSSPixels line_height) { m_inherited.line_height = line_height; }
void set_border_spacing_horizontal(Length border_spacing_horizontal) { m_inherited.border_spacing_horizontal = move(border_spacing_horizontal); }
@ -941,7 +914,6 @@ public:
void set_text_indent(LengthPercentage value) { m_inherited.text_indent = move(value); }
void set_text_wrap_mode(TextWrapMode value) { m_inherited.text_wrap_mode = value; }
void set_text_overflow(TextOverflow value) { m_noninherited.text_overflow = value; }
void set_text_rendering(TextRendering value) { m_inherited.text_rendering = value; }
void set_text_underline_offset(CSSPixels value) { m_inherited.text_underline_offset = value; }
void set_text_underline_position(TextUnderlinePosition value) { m_inherited.text_underline_position = value; }
void set_webkit_text_fill_color(Color value) { m_inherited.webkit_text_fill_color = value; }

View file

@ -204,281 +204,6 @@ Gfx::GlyphRun::TextType InlineLevelIterator::resolve_text_direction_from_context
return Gfx::GlyphRun::TextType::ContextDependent;
}
HashMap<StringView, u8> InlineLevelIterator::shape_features_map() const
{
HashMap<StringView, u8> features;
auto const& computed_values = m_current_node->computed_values();
// 6.4 https://drafts.csswg.org/css-fonts/#font-variant-ligatures-prop
auto ligature_or_null = computed_values.font_variant_ligatures();
auto disable_all_ligatures = [&]() {
features.set("liga"sv, 0);
features.set("clig"sv, 0);
features.set("dlig"sv, 0);
features.set("hlig"sv, 0);
features.set("calt"sv, 0);
};
if (ligature_or_null.has_value()) {
auto ligature = ligature_or_null.release_value();
if (ligature.none) {
// Specifies that all types of ligatures and contextual forms covered by this property are explicitly disabled.
disable_all_ligatures();
} else {
switch (ligature.common) {
case Gfx::FontVariantLigatures::Common::Common:
// Enables display of common ligatures (OpenType features: liga, clig).
features.set("liga"sv, 1);
features.set("clig"sv, 1);
break;
case Gfx::FontVariantLigatures::Common::NoCommon:
// Disables display of common ligatures (OpenType features: liga, clig).
features.set("liga"sv, 0);
features.set("clig"sv, 0);
break;
case Gfx::FontVariantLigatures::Common::Unset:
break;
}
switch (ligature.discretionary) {
case Gfx::FontVariantLigatures::Discretionary::Discretionary:
// Enables display of discretionary ligatures (OpenType feature: dlig).
features.set("dlig"sv, 1);
break;
case Gfx::FontVariantLigatures::Discretionary::NoDiscretionary:
// Disables display of discretionary ligatures (OpenType feature: dlig).
features.set("dlig"sv, 0);
break;
case Gfx::FontVariantLigatures::Discretionary::Unset:
break;
}
switch (ligature.historical) {
case Gfx::FontVariantLigatures::Historical::Historical:
// Enables display of historical ligatures (OpenType feature: hlig).
features.set("hlig"sv, 1);
break;
case Gfx::FontVariantLigatures::Historical::NoHistorical:
// Disables display of historical ligatures (OpenType feature: hlig).
features.set("hlig"sv, 0);
break;
case Gfx::FontVariantLigatures::Historical::Unset:
break;
}
switch (ligature.contextual) {
case Gfx::FontVariantLigatures::Contextual::Contextual:
// Enables display of contextual ligatures (OpenType feature: calt).
features.set("calt"sv, 1);
break;
case Gfx::FontVariantLigatures::Contextual::NoContextual:
// Disables display of contextual ligatures (OpenType feature: calt).
features.set("calt"sv, 0);
break;
case Gfx::FontVariantLigatures::Contextual::Unset:
break;
}
}
} else if (computed_values.text_rendering() == CSS::TextRendering::Optimizespeed) {
// AD-HOC: Disable ligatures if font-variant-ligatures is set to normal and text rendering is set to optimize speed.
disable_all_ligatures();
} else {
// A value of normal specifies that common default features are enabled, as described in detail in the next section.
features.set("liga"sv, 1);
features.set("clig"sv, 1);
}
// 6.5 https://drafts.csswg.org/css-fonts/#font-variant-position-prop
switch (computed_values.font_variant_position()) {
case CSS::FontVariantPosition::Normal:
// None of the features listed below are enabled.
break;
case CSS::FontVariantPosition::Sub:
// Enables display of subscripts (OpenType feature: subs).
features.set("subs"sv, 1);
break;
case CSS::FontVariantPosition::Super:
// Enables display of superscripts (OpenType feature: sups).
features.set("sups"sv, 1);
break;
default:
break;
}
// 6.6 https://drafts.csswg.org/css-fonts/#font-variant-caps-prop
switch (computed_values.font_variant_caps()) {
case CSS::FontVariantCaps::Normal:
// None of the features listed below are enabled.
break;
case CSS::FontVariantCaps::SmallCaps:
// Enables display of small capitals (OpenType feature: smcp). Small-caps glyphs typically use the form of uppercase letters but are reduced to the size of lowercase letters.
features.set("smcp"sv, 1);
break;
case CSS::FontVariantCaps::AllSmallCaps:
// Enables display of small capitals for both upper and lowercase letters (OpenType features: c2sc, smcp).
features.set("c2sc"sv, 1);
features.set("smcp"sv, 1);
break;
case CSS::FontVariantCaps::PetiteCaps:
// Enables display of petite capitals (OpenType feature: pcap).
features.set("pcap"sv, 1);
break;
case CSS::FontVariantCaps::AllPetiteCaps:
// Enables display of petite capitals for both upper and lowercase letters (OpenType features: c2pc, pcap).
features.set("c2pc"sv, 1);
features.set("pcap"sv, 1);
break;
case CSS::FontVariantCaps::Unicase:
// Enables display of mixture of small capitals for uppercase letters with normal lowercase letters (OpenType feature: unic).
features.set("unic"sv, 1);
break;
case CSS::FontVariantCaps::TitlingCaps:
// Enables display of titling capitals (OpenType feature: titl).
features.set("titl"sv, 1);
break;
default:
break;
}
// 6.7 https://drafts.csswg.org/css-fonts/#font-variant-numeric-prop
auto numeric_or_null = computed_values.font_variant_numeric();
if (numeric_or_null.has_value()) {
auto numeric = numeric_or_null.release_value();
if (numeric.figure == Gfx::FontVariantNumeric::Figure::Oldstyle) {
// Enables display of old-style numerals (OpenType feature: onum).
features.set("onum"sv, 1);
} else if (numeric.figure == Gfx::FontVariantNumeric::Figure::Lining) {
// Enables display of lining numerals (OpenType feature: lnum).
features.set("lnum"sv, 1);
}
if (numeric.spacing == Gfx::FontVariantNumeric::Spacing::Proportional) {
// Enables display of proportional numerals (OpenType feature: pnum).
features.set("pnum"sv, 1);
} else if (numeric.spacing == Gfx::FontVariantNumeric::Spacing::Tabular) {
// Enables display of tabular numerals (OpenType feature: tnum).
features.set("tnum"sv, 1);
}
if (numeric.fraction == Gfx::FontVariantNumeric::Fraction::Diagonal) {
// Enables display of diagonal fractions (OpenType feature: frac).
features.set("frac"sv, 1);
} else if (numeric.fraction == Gfx::FontVariantNumeric::Fraction::Stacked) {
// Enables display of stacked fractions (OpenType feature: afrc).
features.set("afrc"sv, 1);
features.set("afrc"sv, 1);
}
if (numeric.ordinal) {
// Enables display of letter forms used with ordinal numbers (OpenType feature: ordn).
features.set("ordn"sv, 1);
}
if (numeric.slashed_zero) {
// Enables display of slashed zeros (OpenType feature: zero).
features.set("zero"sv, 1);
}
}
// 6.10 https://drafts.csswg.org/css-fonts/#font-variant-east-asian-prop
auto east_asian_or_null = computed_values.font_variant_east_asian();
if (east_asian_or_null.has_value()) {
auto east_asian = east_asian_or_null.release_value();
switch (east_asian.variant) {
case Gfx::FontVariantEastAsian::Variant::Jis78:
// Enables display of JIS78 forms (OpenType feature: jp78).
features.set("jp78"sv, 1);
break;
case Gfx::FontVariantEastAsian::Variant::Jis83:
// Enables display of JIS83 forms (OpenType feature: jp83).
features.set("jp83"sv, 1);
break;
case Gfx::FontVariantEastAsian::Variant::Jis90:
// Enables display of JIS90 forms (OpenType feature: jp90).
features.set("jp90"sv, 1);
break;
case Gfx::FontVariantEastAsian::Variant::Jis04:
// Enables display of JIS04 forms (OpenType feature: jp04).
features.set("jp04"sv, 1);
break;
case Gfx::FontVariantEastAsian::Variant::Simplified:
// Enables display of simplified forms (OpenType feature: smpl).
features.set("smpl"sv, 1);
break;
case Gfx::FontVariantEastAsian::Variant::Traditional:
// Enables display of traditional forms (OpenType feature: trad).
features.set("trad"sv, 1);
break;
default:
break;
}
switch (east_asian.width) {
case Gfx::FontVariantEastAsian::Width::FullWidth:
// Enables display of full-width forms (OpenType feature: fwid).
features.set("fwid"sv, 1);
break;
case Gfx::FontVariantEastAsian::Width::Proportional:
// Enables display of proportional-width forms (OpenType feature: pwid).
features.set("pwid"sv, 1);
break;
default:
break;
}
if (east_asian.ruby) {
// Enables display of ruby forms (OpenType feature: ruby).
features.set("ruby"sv, 1);
}
}
// FIXME: vkrn should be enabled for vertical text.
switch (computed_values.font_kerning()) {
case CSS::FontKerning::Auto:
// AD-HOC: Disable kerning if font-kerning is set to normal and text rendering is set to optimize speed.
features.set("kern"sv, computed_values.text_rendering() != CSS::TextRendering::Optimizespeed ? 1 : 0);
break;
case CSS::FontKerning::Normal:
features.set("kern"sv, 1);
break;
case CSS::FontKerning::None:
features.set("kern"sv, 0);
break;
default:
break;
}
return features;
}
Gfx::ShapeFeatures InlineLevelIterator::create_and_merge_font_features() const
{
HashMap<StringView, u8> merged_features;
auto const& computed_values = m_inline_formatting_context.containing_block().computed_values();
// https://www.w3.org/TR/css-fonts-3/#feature-precedence
// FIXME 1. Font features enabled by default, including features required for a given script.
// FIXME 2. If the font is defined via an @font-face rule, the font features implied by the font-feature-settings descriptor in the @font-face rule.
// 3. Font features implied by the value of the font-variant property, the related font-variant subproperties and any other CSS property that uses OpenType features (e.g. the font-kerning property).
merged_features.update(shape_features_map());
// FIXME 4. Feature settings determined by properties other than font-variant or font-feature-settings. For example, setting a non-default value for the letter-spacing property disables common ligatures.
// 5. Font features implied by the value of font-feature-settings property.
merged_features.update(computed_values.font_feature_settings());
Gfx::ShapeFeatures shape_features;
shape_features.ensure_capacity(merged_features.size());
for (auto& it : merged_features) {
shape_features.unchecked_append({ { it.key[0], it.key[1], it.key[2], it.key[3] }, static_cast<u32>(it.value) });
}
return shape_features;
}
Optional<InlineLevelIterator::Item> InlineLevelIterator::next_without_lookahead()
{
if (!m_current_node)
@ -578,7 +303,7 @@ Optional<InlineLevelIterator::Item> InlineLevelIterator::next_without_lookahead(
x = tab_stop_dist.to_float();
}
auto shape_features = create_and_merge_font_features();
auto shape_features = text_node->computed_values().font_features();
auto glyph_run = Gfx::shape_text({ x, 0 }, letter_spacing.to_float(), chunk.view, chunk.font, text_type, shape_features);
CSSPixels chunk_width = CSSPixels::nearest_value_for(glyph_run->width() + x);

View file

@ -67,9 +67,6 @@ private:
void add_extra_box_model_metrics_to_item(Item&, bool add_leading_metrics, bool add_trailing_metrics);
HashMap<StringView, u8> shape_features_map() const;
Gfx::ShapeFeatures create_and_merge_font_features() const;
Layout::Node const* next_inline_node_in_pre_order(Layout::Node const& current, Layout::Node const* stay_within);
Layout::InlineFormattingContext& m_inline_formatting_context;

View file

@ -382,7 +382,6 @@ 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.font_size());
computed_values.set_font_weight(computed_style.font_weight());
computed_values.set_font_kerning(computed_style.font_kerning());
computed_values.set_line_height(computed_style.line_height());
// NOTE: color must be set after color-scheme to ensure currentColor can be resolved in other properties (e.g. background-color).
@ -528,18 +527,7 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style)
if (auto maybe_font_language_override = computed_style.font_language_override(); maybe_font_language_override.has_value())
computed_values.set_font_language_override(maybe_font_language_override.release_value());
if (auto maybe_font_variant_alternates = computed_style.font_variant_alternates(); maybe_font_variant_alternates.has_value())
computed_values.set_font_variant_alternates(maybe_font_variant_alternates.release_value());
computed_values.set_font_variant_caps(computed_style.font_variant_caps());
if (auto maybe_font_variant_east_asian = computed_style.font_variant_east_asian(); maybe_font_variant_east_asian.has_value())
computed_values.set_font_variant_east_asian(maybe_font_variant_east_asian.release_value());
computed_values.set_font_variant_emoji(computed_style.font_variant_emoji());
if (auto maybe_font_variant_ligatures = computed_style.font_variant_ligatures(); maybe_font_variant_ligatures.has_value())
computed_values.set_font_variant_ligatures(maybe_font_variant_ligatures.release_value());
if (auto maybe_font_variant_numeric = computed_style.font_variant_numeric(); maybe_font_variant_numeric.has_value())
computed_values.set_font_variant_numeric(maybe_font_variant_numeric.release_value());
computed_values.set_font_feature_settings(computed_style.font_feature_settings());
computed_values.set_font_variant_position(computed_style.font_variant_position());
computed_values.set_font_features(computed_style.font_features());
if (auto maybe_font_variation_settings = computed_style.font_variation_settings(); maybe_font_variation_settings.has_value())
computed_values.set_font_variation_settings(maybe_font_variation_settings.release_value());
@ -609,7 +597,6 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style)
computed_values.set_text_align(computed_style.text_align());
computed_values.set_text_justify(computed_style.text_justify());
computed_values.set_text_overflow(computed_style.text_overflow());
computed_values.set_text_rendering(computed_style.text_rendering());
computed_values.set_text_underline_offset(computed_style.text_underline_offset());
computed_values.set_text_underline_position(computed_style.text_underline_position());