mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
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:
parent
7a5b948b5b
commit
bbb344d534
Notes:
github-actions[bot]
2025-11-10 11:13:01 +00:00
Author: https://github.com/Calme1709
Commit: bbb344d534
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6773
6 changed files with 278 additions and 325 deletions
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue