Skip to content

Commit e43f3e4

Browse files
committed
LibWeb/CSS: Parse font-[feature,variation]-settings descriptors
1 parent 95c17df commit e43f3e4

File tree

5 files changed

+102
-3
lines changed

5 files changed

+102
-3
lines changed

Userland/Libraries/LibWeb/CSS/FontFace.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,8 @@ void FontFace::load_font_source()
402402
FontDisplay::Auto, // FIXME: font_display
403403
{}, // font-named-instance doesn't exist in FontFace
404404
{}, // font-language-override doesn't exist in FontFace
405+
{}, // FIXME: feature_settings
406+
{}, // FIXME: variation_settings
405407
};
406408
if (auto loader = style_computer.load_font_face(parsed_font_face, move(on_load), move(on_error)); loader.has_value())
407409
loader->start_loading_next_url();

Userland/Libraries/LibWeb/CSS/ParsedFontFace.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
namespace Web::CSS {
1111

12-
ParsedFontFace::ParsedFontFace(FlyString font_family, Optional<int> weight, Optional<int> slope, Optional<int> width, Vector<Source> sources, Vector<Gfx::UnicodeRange> unicode_ranges, Optional<Percentage> ascent_override, Optional<Percentage> descent_override, Optional<Percentage> line_gap_override, FontDisplay font_display, Optional<FlyString> font_named_instance, Optional<FlyString> font_language_override)
12+
ParsedFontFace::ParsedFontFace(FlyString font_family, Optional<int> weight, Optional<int> slope, Optional<int> width, Vector<Source> sources, Vector<Gfx::UnicodeRange> unicode_ranges, Optional<Percentage> ascent_override, Optional<Percentage> descent_override, Optional<Percentage> line_gap_override, FontDisplay font_display, Optional<FlyString> font_named_instance, Optional<FlyString> font_language_override, Optional<OrderedHashMap<FlyString, i64>> font_feature_settings, Optional<OrderedHashMap<FlyString, double>> font_variation_settings)
1313
: m_font_family(move(font_family))
1414
, m_font_named_instance(move(font_named_instance))
1515
, m_weight(weight)
@@ -22,6 +22,8 @@ ParsedFontFace::ParsedFontFace(FlyString font_family, Optional<int> weight, Opti
2222
, m_line_gap_override(move(line_gap_override))
2323
, m_font_display(font_display)
2424
, m_font_language_override(font_language_override)
25+
, m_font_feature_settings(move(font_feature_settings))
26+
, m_font_variation_settings(move(font_variation_settings))
2527
{
2628
}
2729

Userland/Libraries/LibWeb/CSS/ParsedFontFace.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#pragma once
99

1010
#include <AK/FlyString.h>
11+
#include <AK/HashMap.h>
1112
#include <LibGfx/Font/UnicodeRange.h>
1213
#include <LibURL/URL.h>
1314
#include <LibWeb/CSS/Enums.h>
@@ -23,15 +24,17 @@ class ParsedFontFace {
2324
Optional<FlyString> format;
2425
};
2526

26-
ParsedFontFace(FlyString font_family, Optional<int> weight, Optional<int> slope, Optional<int> width, Vector<Source> sources, Vector<Gfx::UnicodeRange> unicode_ranges, Optional<Percentage> ascent_override, Optional<Percentage> descent_override, Optional<Percentage> line_gap_override, FontDisplay font_display, Optional<FlyString> font_named_instance, Optional<FlyString> font_language_override);
27+
ParsedFontFace(FlyString font_family, Optional<int> weight, Optional<int> slope, Optional<int> width, Vector<Source> sources, Vector<Gfx::UnicodeRange> unicode_ranges, Optional<Percentage> ascent_override, Optional<Percentage> descent_override, Optional<Percentage> line_gap_override, FontDisplay font_display, Optional<FlyString> font_named_instance, Optional<FlyString> font_language_override, Optional<OrderedHashMap<FlyString, i64>> font_feature_settings, Optional<OrderedHashMap<FlyString, double>> font_variation_settings);
2728
~ParsedFontFace() = default;
2829

2930
Optional<Percentage> ascent_override() const { return m_ascent_override; }
3031
Optional<Percentage> descent_override() const { return m_descent_override; }
3132
FontDisplay font_display() const { return m_font_display; }
3233
FlyString font_family() const { return m_font_family; }
34+
Optional<OrderedHashMap<FlyString, i64>> font_feature_settings() const { return m_font_feature_settings; }
3335
Optional<FlyString> font_language_override() const { return m_font_language_override; }
3436
Optional<FlyString> font_named_instance() const { return m_font_named_instance; }
37+
Optional<OrderedHashMap<FlyString, double>> font_variation_settings() const { return m_font_variation_settings; }
3538
Optional<int> slope() const { return m_slope; }
3639
Optional<int> weight() const { return m_weight; }
3740
Optional<int> width() const { return m_width; }
@@ -52,6 +55,8 @@ class ParsedFontFace {
5255
Optional<Percentage> m_line_gap_override;
5356
FontDisplay m_font_display;
5457
Optional<FlyString> m_font_language_override;
58+
Optional<OrderedHashMap<FlyString, i64>> m_font_feature_settings;
59+
Optional<OrderedHashMap<FlyString, double>> m_font_variation_settings;
5560
};
5661

5762
}

Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5615,6 +5615,8 @@ JS::GCPtr<CSSFontFaceRule> Parser::parse_font_face_rule(TokenStream<ComponentVal
56155615
Optional<Percentage> line_gap_override;
56165616
FontDisplay font_display = FontDisplay::Auto;
56175617
Optional<FlyString> language_override;
5618+
Optional<OrderedHashMap<FlyString, i64>> font_feature_settings;
5619+
Optional<OrderedHashMap<FlyString, double>> font_variation_settings;
56185620

56195621
// "normal" is returned as nullptr
56205622
auto parse_as_percentage_or_normal = [&](Vector<ComponentValue> const& values) -> ErrorOr<Optional<Percentage>> {
@@ -5734,6 +5736,40 @@ JS::GCPtr<CSSFontFaceRule> Parser::parse_font_face_rule(TokenStream<ComponentVal
57345736
font_family = String::join(' ', font_family_parts).release_value_but_fixme_should_propagate_errors();
57355737
continue;
57365738
}
5739+
if (declaration.name().equals_ignoring_ascii_case("font-feature-settings"sv)) {
5740+
TokenStream token_stream { declaration.values() };
5741+
if (auto value = parse_css_value(CSS::PropertyID::FontFeatureSettings, token_stream); !value.is_error()) {
5742+
if (value.value()->to_keyword() == Keyword::Normal) {
5743+
font_feature_settings.clear();
5744+
} else if (value.value()->is_value_list()) {
5745+
auto const& feature_tags = value.value()->as_value_list().values();
5746+
OrderedHashMap<FlyString, i64> settings;
5747+
settings.ensure_capacity(feature_tags.size());
5748+
for (auto const& feature_tag : feature_tags) {
5749+
if (!feature_tag->is_open_type_tagged()) {
5750+
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Value in font-feature-settings descriptor is not an OpenTypeTaggedStyleValue; skipping");
5751+
continue;
5752+
}
5753+
auto const& setting_value = feature_tag->as_open_type_tagged().value();
5754+
if (setting_value->is_integer()) {
5755+
settings.set(feature_tag->as_open_type_tagged().tag(), setting_value->as_integer().integer());
5756+
} else if (setting_value->is_math() && setting_value->as_math().resolves_to_number()) {
5757+
if (auto integer = setting_value->as_math().resolve_integer(); integer.has_value()) {
5758+
settings.set(feature_tag->as_open_type_tagged().tag(), *integer);
5759+
} else {
5760+
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Calculated value in font-feature-settings descriptor cannot be resolved at parse time; skipping");
5761+
}
5762+
} else {
5763+
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Value in font-feature-settings descriptor is not an OpenTypeTaggedStyleValue holding a <integer>; skipping");
5764+
}
5765+
}
5766+
font_feature_settings = move(settings);
5767+
} else {
5768+
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Failed to parse font-feature-settings descriptor, not compatible with value returned from parsing font-feature-settings property: {}", value.value()->to_string());
5769+
}
5770+
}
5771+
continue;
5772+
}
57375773
if (declaration.name().equals_ignoring_ascii_case("font-language-override"sv)) {
57385774
TokenStream token_stream { declaration.values() };
57395775
if (auto maybe_value = parse_css_value(CSS::PropertyID::FontLanguageOverride, token_stream); !maybe_value.is_error()) {
@@ -5774,6 +5810,40 @@ JS::GCPtr<CSSFontFaceRule> Parser::parse_font_face_rule(TokenStream<ComponentVal
57745810
}
57755811
continue;
57765812
}
5813+
if (declaration.name().equals_ignoring_ascii_case("font-variation-settings"sv)) {
5814+
TokenStream token_stream { declaration.values() };
5815+
if (auto value = parse_css_value(CSS::PropertyID::FontVariationSettings, token_stream); !value.is_error()) {
5816+
if (value.value()->to_keyword() == Keyword::Normal) {
5817+
font_variation_settings.clear();
5818+
} else if (value.value()->is_value_list()) {
5819+
auto const& variation_tags = value.value()->as_value_list().values();
5820+
OrderedHashMap<FlyString, double> settings;
5821+
settings.ensure_capacity(variation_tags.size());
5822+
for (auto const& variation_tag : variation_tags) {
5823+
if (!variation_tag->is_open_type_tagged()) {
5824+
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Value in font-variation-settings descriptor is not an OpenTypeTaggedStyleValue; skipping");
5825+
continue;
5826+
}
5827+
auto const& setting_value = variation_tag->as_open_type_tagged().value();
5828+
if (setting_value->is_number()) {
5829+
settings.set(variation_tag->as_open_type_tagged().tag(), setting_value->as_number().number());
5830+
} else if (setting_value->is_math() && setting_value->as_math().resolves_to_number()) {
5831+
if (auto number = setting_value->as_math().resolve_number(); number.has_value()) {
5832+
settings.set(variation_tag->as_open_type_tagged().tag(), *number);
5833+
} else {
5834+
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Calculated value in font-variation-settings descriptor cannot be resolved at parse time; skipping");
5835+
}
5836+
} else {
5837+
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Value in font-variation-settings descriptor is not an OpenTypeTaggedStyleValue holding a <number>; skipping");
5838+
}
5839+
}
5840+
font_variation_settings = move(settings);
5841+
} else {
5842+
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Failed to parse font-variation-settings descriptor, not compatible with value returned from parsing font-variation-settings property: {}", value.value()->to_string());
5843+
}
5844+
}
5845+
continue;
5846+
}
57775847
if (declaration.name().equals_ignoring_ascii_case("font-weight"sv)) {
57785848
TokenStream token_stream { declaration.values() };
57795849
if (auto value = parse_css_value(CSS::PropertyID::FontWeight, token_stream); !value.is_error()) {
@@ -5827,7 +5897,7 @@ JS::GCPtr<CSSFontFaceRule> Parser::parse_font_face_rule(TokenStream<ComponentVal
58275897
unicode_range.empend(0x0u, 0x10FFFFu);
58285898
}
58295899

5830-
return CSSFontFaceRule::create(m_context.realm(), ParsedFontFace { font_family.release_value(), move(weight), move(slope), move(width), move(src), move(unicode_range), move(ascent_override), move(descent_override), move(line_gap_override), font_display, move(font_named_instance), move(language_override) });
5900+
return CSSFontFaceRule::create(m_context.realm(), ParsedFontFace { font_family.release_value(), move(weight), move(slope), move(width), move(src), move(unicode_range), move(ascent_override), move(descent_override), move(line_gap_override), font_display, move(font_named_instance), move(language_override), move(font_feature_settings), move(font_variation_settings) });
58315901
}
58325902

58335903
Vector<ParsedFontFace::Source> Parser::parse_as_font_face_src()

Userland/Libraries/LibWeb/Dump.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,26 @@ void dump_font_face_rule(StringBuilder& builder, CSS::CSSFontFaceRule const& rul
709709
indent(builder, indent_levels + 1);
710710
builder.appendff("language-override: {}\n", font_face.font_language_override().value());
711711
}
712+
713+
if (font_face.font_feature_settings().has_value()) {
714+
indent(builder, indent_levels + 1);
715+
builder.append("feature-settings:"sv);
716+
auto const& entries = font_face.font_feature_settings().value();
717+
for (auto const& [name, value] : entries) {
718+
builder.appendff(" {}={}", name, value);
719+
}
720+
builder.append("\n"sv);
721+
}
722+
723+
if (font_face.font_variation_settings().has_value()) {
724+
indent(builder, indent_levels + 1);
725+
builder.append("variation-settings:"sv);
726+
auto const& entries = font_face.font_variation_settings().value();
727+
for (auto const& [name, value] : entries) {
728+
builder.appendff(" {}={}", name, value);
729+
}
730+
builder.append("\n"sv);
731+
}
712732
}
713733

714734
void dump_import_rule(StringBuilder& builder, CSS::CSSImportRule const& rule, int indent_levels)

0 commit comments

Comments
 (0)