mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-20 11:36:10 +00:00
LibWeb/CSS: Serialize @font-face
closer to spec
Read the descriptor style values instead of producing a ParsedFontFace first, as this means we know if a descriptor is actually present, or has been defaulted to an initial value. This lets us correctly skip the unicode-range if it was not explicitly set. Firefox and Chromium both serialize using the "font-stretch" name, (which is an alias for font-width) which follows the outdated cssom spec, so I've done so too to match them. The one thing that we still do differently in this test is that those browsers check explicitly if `font-stretch` was set, and ignore when `font-width` is. I've also inlined the `serialize_a_local()` function to the one place it's used. The style value to_string() method was already wrapping the string in quotes, so calling serialize_a_string() on it was producing `local("\this mess\"")`. It's clearer what's happening when the code isn't split up.
This commit is contained in:
parent
a7f7c2a821
commit
ee647616b2
Notes:
github-actions[bot]
2025-04-07 09:02:33 +00:00
Author: https://github.com/AtkinsSJ Commit: https://github.com/LadybirdBrowser/ladybird/commit/ee647616b2e Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4224
5 changed files with 33 additions and 114 deletions
|
@ -51,7 +51,8 @@ ParsedFontFace CSSFontFaceRule::font_face() const
|
|||
// https://www.w3.org/TR/cssom/#ref-for-cssfontfacerule
|
||||
String CSSFontFaceRule::serialized() const
|
||||
{
|
||||
auto font_face = this->font_face();
|
||||
auto& descriptors = *m_style;
|
||||
|
||||
StringBuilder builder;
|
||||
// The result of concatenating the following:
|
||||
|
||||
|
@ -62,44 +63,29 @@ String CSSFontFaceRule::serialized() const
|
|||
builder.append("font-family: "sv);
|
||||
|
||||
// 3. The result of performing serialize a string on the rule’s font family name.
|
||||
serialize_a_string(builder, font_face.font_family());
|
||||
builder.append(descriptors.descriptor(DescriptorID::FontFamily)->to_string(CSSStyleValue::SerializationMode::Normal));
|
||||
|
||||
// 4. The string ";", i.e., SEMICOLON (U+003B).
|
||||
builder.append(';');
|
||||
|
||||
// 5. If the rule’s associated source list is not empty, follow these substeps:
|
||||
if (!font_face.sources().is_empty()) {
|
||||
if (auto sources = descriptors.descriptor(DescriptorID::Src)) {
|
||||
// 1. A single SPACE (U+0020), followed by the string "src:", followed by a single SPACE (U+0020).
|
||||
builder.append(" src: "sv);
|
||||
|
||||
// 2. The result of invoking serialize a comma-separated list on performing serialize a URL or serialize a LOCAL for each source on the source list.
|
||||
serialize_a_comma_separated_list(builder, font_face.sources(), [&](StringBuilder& builder, ParsedFontFace::Source source) -> void {
|
||||
source.local_or_url.visit(
|
||||
[&builder](URL::URL const& url) {
|
||||
serialize_a_url(builder, url.to_string());
|
||||
},
|
||||
[&builder](FlyString const& local) {
|
||||
// https://drafts.csswg.org/cssom-1/#serialize-a-local
|
||||
// To serialize a LOCAL means to create a string represented by "local(", followed by the serialization of the LOCAL as a string, followed by ")".
|
||||
builder.appendff("local({})", serialize_a_string(local));
|
||||
});
|
||||
|
||||
// NOTE: No spec currently exists for format()
|
||||
if (source.format.has_value()) {
|
||||
builder.append(" format("sv);
|
||||
serialize_a_string(builder, source.format.value());
|
||||
builder.append(")"sv);
|
||||
}
|
||||
});
|
||||
builder.append(sources->to_string(CSSStyleValue::SerializationMode::Normal));
|
||||
|
||||
// 3. The string ";", i.e., SEMICOLON (U+003B).
|
||||
builder.append(';');
|
||||
}
|
||||
|
||||
// 6. If rule’s associated unicode-range descriptor is present, a single SPACE (U+0020), followed by the string "unicode-range:", followed by a single SPACE (U+0020), followed by the result of performing serialize a <'unicode-range'>, followed by the string ";", i.e., SEMICOLON (U+003B).
|
||||
builder.append(" unicode-range: "sv);
|
||||
serialize_unicode_ranges(builder, font_face.unicode_ranges());
|
||||
builder.append(';');
|
||||
if (auto unicode_range = descriptors.descriptor(DescriptorID::UnicodeRange)) {
|
||||
builder.append(" unicode-range: "sv);
|
||||
builder.append(unicode_range->to_string(CSSStyleValue::SerializationMode::Normal));
|
||||
builder.append(';');
|
||||
}
|
||||
|
||||
// FIXME: 7. If rule’s associated font-variant descriptor is present, a single SPACE (U+0020),
|
||||
// followed by the string "font-variant:", followed by a single SPACE (U+0020),
|
||||
|
@ -110,23 +96,9 @@ String CSSFontFaceRule::serialized() const
|
|||
// followed by the string "font-feature-settings:", followed by a single SPACE (U+0020),
|
||||
// followed by the result of performing serialize a <'font-feature-settings'>,
|
||||
// followed by the string ";", i.e., SEMICOLON (U+003B).
|
||||
if (font_face.font_feature_settings().has_value()) {
|
||||
auto const& feature_settings = font_face.font_feature_settings().value();
|
||||
if (auto font_feature_settings = descriptors.descriptor(DescriptorID::FontFeatureSettings)) {
|
||||
builder.append(" font-feature-settings: "sv);
|
||||
// NOTE: We sort the tags during parsing, so they're already in the correct order.
|
||||
bool first = true;
|
||||
for (auto const& [key, value] : feature_settings) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
builder.append(", "sv);
|
||||
}
|
||||
|
||||
serialize_a_string(builder, key);
|
||||
// NOTE: 1 is the default value, so don't serialize it.
|
||||
if (value != 1)
|
||||
builder.appendff(" {}", value);
|
||||
}
|
||||
builder.append(font_feature_settings->to_string(CSSStyleValue::SerializationMode::Normal));
|
||||
builder.append(";"sv);
|
||||
}
|
||||
|
||||
|
@ -135,43 +107,9 @@ String CSSFontFaceRule::serialized() const
|
|||
// followed by the result of performing serialize a <'font-stretch'>,
|
||||
// followed by the string ";", i.e., SEMICOLON (U+003B).
|
||||
// NOTE: font-stretch is now an alias for font-width, so we use that instead.
|
||||
if (font_face.width().has_value()) {
|
||||
builder.append(" font-width: "sv);
|
||||
// NOTE: font-width is supposed to always be serialized as a percentage.
|
||||
// Right now, it's stored as a Gfx::FontWidth value, so we have to lossily convert it back.
|
||||
float percentage = 100.0f;
|
||||
switch (font_face.width().value()) {
|
||||
case Gfx::FontWidth::UltraCondensed:
|
||||
percentage = 50.0f;
|
||||
break;
|
||||
case Gfx::FontWidth::ExtraCondensed:
|
||||
percentage = 62.5f;
|
||||
break;
|
||||
case Gfx::FontWidth::Condensed:
|
||||
percentage = 75.0f;
|
||||
break;
|
||||
case Gfx::FontWidth::SemiCondensed:
|
||||
percentage = 87.5f;
|
||||
break;
|
||||
case Gfx::FontWidth::Normal:
|
||||
percentage = 100.0f;
|
||||
break;
|
||||
case Gfx::FontWidth::SemiExpanded:
|
||||
percentage = 112.5f;
|
||||
break;
|
||||
case Gfx::FontWidth::Expanded:
|
||||
percentage = 125.0f;
|
||||
break;
|
||||
case Gfx::FontWidth::ExtraExpanded:
|
||||
percentage = 150.0f;
|
||||
break;
|
||||
case Gfx::FontWidth::UltraExpanded:
|
||||
percentage = 200.0f;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
builder.appendff("{}%", percentage);
|
||||
if (auto font_width = descriptors.descriptor(DescriptorID::FontWidth)) {
|
||||
builder.append(" font-stretch: "sv);
|
||||
builder.append(font_width->to_string(CSSStyleValue::SerializationMode::Normal));
|
||||
builder.append(";"sv);
|
||||
}
|
||||
|
||||
|
@ -179,15 +117,9 @@ String CSSFontFaceRule::serialized() const
|
|||
// followed by the string "font-weight:", followed by a single SPACE (U+0020),
|
||||
// followed by the result of performing serialize a <'font-weight'>,
|
||||
// followed by the string ";", i.e., SEMICOLON (U+003B).
|
||||
if (font_face.weight().has_value()) {
|
||||
auto weight = font_face.weight().value();
|
||||
if (auto font_weight = descriptors.descriptor(DescriptorID::FontWeight)) {
|
||||
builder.append(" font-weight: "sv);
|
||||
if (weight == 400)
|
||||
builder.append("normal"sv);
|
||||
else if (weight == 700)
|
||||
builder.append("bold"sv);
|
||||
else
|
||||
builder.appendff("{}", weight);
|
||||
builder.append(font_weight->to_string(CSSStyleValue::SerializationMode::Normal));
|
||||
builder.append(";"sv);
|
||||
}
|
||||
|
||||
|
@ -195,17 +127,9 @@ String CSSFontFaceRule::serialized() const
|
|||
// followed by the string "font-style:", followed by a single SPACE (U+0020),
|
||||
// followed by the result of performing serialize a <'font-style'>,
|
||||
// followed by the string ";", i.e., SEMICOLON (U+003B).
|
||||
if (font_face.slope().has_value()) {
|
||||
auto slope = font_face.slope().value();
|
||||
if (auto font_style = descriptors.descriptor(DescriptorID::FontStyle)) {
|
||||
builder.append(" font-style: "sv);
|
||||
if (slope == Gfx::name_to_slope("Normal"sv))
|
||||
builder.append("normal"sv);
|
||||
else if (slope == Gfx::name_to_slope("Italic"sv))
|
||||
builder.append("italic"sv);
|
||||
else {
|
||||
dbgln("FIXME: CSSFontFaceRule::serialized() does not support slope {}", slope);
|
||||
builder.append("italic"sv);
|
||||
}
|
||||
builder.append(font_style->to_string(CSSStyleValue::SerializationMode::Normal));
|
||||
builder.append(";"sv);
|
||||
}
|
||||
|
||||
|
|
|
@ -117,16 +117,6 @@ void serialize_a_url(StringBuilder& builder, StringView url)
|
|||
builder.append(')');
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/cssom-1/#serialize-a-local
|
||||
void serialize_a_local(StringBuilder& builder, StringView path)
|
||||
{
|
||||
// To serialize a LOCAL means to create a string represented by "local(",
|
||||
// followed by the serialization of the LOCAL as a string, followed by ")".
|
||||
builder.append("local("sv);
|
||||
serialize_a_string(builder, path);
|
||||
builder.append(')');
|
||||
}
|
||||
|
||||
// NOTE: No spec currently exists for serializing a <'unicode-range'>.
|
||||
void serialize_unicode_ranges(StringBuilder& builder, Vector<Gfx::UnicodeRange> const& unicode_ranges)
|
||||
{
|
||||
|
|
|
@ -21,7 +21,6 @@ void escape_a_character_as_code_point(StringBuilder&, u32 character);
|
|||
void serialize_an_identifier(StringBuilder&, StringView ident);
|
||||
void serialize_a_string(StringBuilder&, StringView string);
|
||||
void serialize_a_url(StringBuilder&, StringView url);
|
||||
void serialize_a_local(StringBuilder&, StringView path);
|
||||
void serialize_unicode_ranges(StringBuilder&, Vector<Gfx::UnicodeRange> const& unicode_ranges);
|
||||
void serialize_a_srgb_value(StringBuilder&, Color color);
|
||||
|
||||
|
|
|
@ -24,8 +24,14 @@ String FontSourceStyleValue::to_string(SerializationMode) const
|
|||
return m_source.visit(
|
||||
[](Local const& local) {
|
||||
// local(<family-name>)
|
||||
|
||||
// https://www.w3.org/TR/cssom-1/#serialize-a-local
|
||||
// To serialize a LOCAL means to create a string represented by "local(",
|
||||
// followed by the serialization of the LOCAL as a string, followed by ")".
|
||||
StringBuilder builder;
|
||||
serialize_a_local(builder, local.name->to_string(SerializationMode::Normal));
|
||||
builder.append("local("sv);
|
||||
builder.append(local.name->to_string(SerializationMode::Normal));
|
||||
builder.append(')');
|
||||
return builder.to_string_without_validation();
|
||||
},
|
||||
[this](URL::URL const& url) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@font-face { font-family: "a1"; src: local("xyz"); unicode-range: "U+0-10ffff"; }
|
||||
@font-face { font-family: "b1"; unicode-range: "U+0-10ffff"; font-feature-settings: "aaaa", "bbbb" 0, "yyyy" 3, "zzzz"; }
|
||||
@font-face { font-family: "b2"; unicode-range: "U+0-10ffff"; font-feature-settings: "aaaa" 3; }
|
||||
@font-face { font-family: "c1"; unicode-range: "U+0-10ffff"; font-width: 62.5%; }
|
||||
@font-face { font-family: "c2"; unicode-range: "U+0-10ffff"; font-width: 50%; }
|
||||
@font-face { font-family: "c3"; unicode-range: "U+0-10ffff"; font-width: 62.5%; }
|
||||
@font-face { font-family: "c4"; unicode-range: "U+0-10ffff"; font-width: 50%; }
|
||||
@font-face { font-family: "a1"; src: local("xyz"); }
|
||||
@font-face { font-family: "b1"; font-feature-settings: "aaaa", "bbbb" 0, "yyyy" 3, "zzzz"; }
|
||||
@font-face { font-family: "b2"; font-feature-settings: "aaaa" 3; }
|
||||
@font-face { font-family: "c1"; font-stretch: extra-condensed; }
|
||||
@font-face { font-family: "c2"; font-stretch: 50%; }
|
||||
@font-face { font-family: "c3"; font-stretch: extra-condensed; }
|
||||
@font-face { font-family: "c4"; font-stretch: 50%; }
|
||||
|
|
Loading…
Add table
Reference in a new issue