LibWeb: Parse oblique font-style with an angle value

This commit is contained in:
Tim Ledbetter 2025-05-02 13:55:58 +01:00 committed by Andreas Kling
commit c0f9b11070
Notes: github-actions[bot] 2025-05-03 10:06:24 +00:00
13 changed files with 172 additions and 31 deletions

View file

@ -173,6 +173,7 @@ set(SOURCES
CSS/StyleValues/EdgeStyleValue.cpp
CSS/StyleValues/FilterValueListStyleValue.cpp
CSS/StyleValues/FontSourceStyleValue.cpp
CSS/StyleValues/FontStyleStyleValue.cpp
CSS/StyleValues/GridAutoFlowStyleValue.cpp
CSS/StyleValues/GridTemplateAreaStyleValue.cpp
CSS/StyleValues/GridTrackPlacementStyleValue.cpp

View file

@ -34,6 +34,7 @@
#include <LibWeb/CSS/StyleValues/FitContentStyleValue.h>
#include <LibWeb/CSS/StyleValues/FlexStyleValue.h>
#include <LibWeb/CSS/StyleValues/FontSourceStyleValue.h>
#include <LibWeb/CSS/StyleValues/FontStyleStyleValue.h>
#include <LibWeb/CSS/StyleValues/FrequencyStyleValue.h>
#include <LibWeb/CSS/StyleValues/GridAutoFlowStyleValue.h>
#include <LibWeb/CSS/StyleValues/GridTemplateAreaStyleValue.h>
@ -203,6 +204,12 @@ FontSourceStyleValue const& CSSStyleValue::as_font_source() const
return static_cast<FontSourceStyleValue const&>(*this);
}
FontStyleStyleValue const& CSSStyleValue::as_font_style() const
{
VERIFY(is_font_style());
return static_cast<FontStyleStyleValue const&>(*this);
}
FrequencyStyleValue const& CSSStyleValue::as_frequency() const
{
VERIFY(is_frequency());

View file

@ -107,6 +107,7 @@ public:
FitContent,
Flex,
FontSource,
FontStyle,
FontVariant,
Frequency,
GridAutoFlow,
@ -234,6 +235,10 @@ public:
FontSourceStyleValue const& as_font_source() const;
FontSourceStyleValue& as_font_source() { return const_cast<FontSourceStyleValue&>(const_cast<CSSStyleValue const&>(*this).as_font_source()); }
bool is_font_style() const { return type() == Type::FontStyle; }
FontStyleStyleValue const& as_font_style() const;
FontStyleStyleValue& as_font_style() { return const_cast<FontStyleStyleValue&>(const_cast<CSSStyleValue const&>(*this).as_font_style()); }
bool is_frequency() const { return type() == Type::Frequency; }
FrequencyStyleValue const& as_frequency() const;
FrequencyStyleValue& as_frequency() { return const_cast<FrequencyStyleValue&>(const_cast<CSSStyleValue const&>(*this).as_frequency()); }

View file

@ -247,6 +247,13 @@
"fallback",
"optional"
],
"font-style": [
"normal",
"italic",
"left",
"right",
"oblique"
],
"font-variant-alternates": [
"normal",
"historical-forms"

View file

@ -394,6 +394,7 @@ private:
RefPtr<CSSStyleValue const> parse_font_family_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue const> parse_font_language_override_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue const> parse_font_feature_settings_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue const> parse_font_style_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue const> parse_font_variation_settings_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue const> parse_font_variant(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue const> parse_font_variant_alternates_value(TokenStream<ComponentValue>&);

View file

@ -34,6 +34,7 @@
#include <LibWeb/CSS/StyleValues/FilterValueListStyleValue.h>
#include <LibWeb/CSS/StyleValues/FitContentStyleValue.h>
#include <LibWeb/CSS/StyleValues/FlexStyleValue.h>
#include <LibWeb/CSS/StyleValues/FontStyleStyleValue.h>
#include <LibWeb/CSS/StyleValues/FrequencyStyleValue.h>
#include <LibWeb/CSS/StyleValues/GridAutoFlowStyleValue.h>
#include <LibWeb/CSS/StyleValues/GridTemplateAreaStyleValue.h>
@ -523,6 +524,10 @@ Parser::ParseErrorOr<NonnullRefPtr<CSSStyleValue const>> Parser::parse_css_value
if (auto parsed_value = parse_font_language_override_value(tokens); parsed_value && !tokens.has_next_token())
return parsed_value.release_nonnull();
return ParseError::SyntaxError;
case PropertyID::FontStyle:
if (auto parsed_value = parse_font_style_value(tokens); parsed_value && !tokens.has_next_token())
return parsed_value.release_nonnull();
return ParseError::SyntaxError;
case PropertyID::FontVariationSettings:
if (auto parsed_value = parse_font_variation_settings_value(tokens); parsed_value && !tokens.has_next_token())
return parsed_value.release_nonnull();
@ -2361,7 +2366,7 @@ RefPtr<CSSStyleValue const> Parser::parse_font_value(TokenStream<ComponentValue>
}
case PropertyID::FontStyle: {
VERIFY(!font_style);
font_style = value.release_nonnull();
font_style = FontStyleStyleValue::create(*keyword_to_font_style(value.release_nonnull()->to_keyword()));
continue;
}
case PropertyID::FontVariant: {
@ -2597,6 +2602,34 @@ RefPtr<CSSStyleValue const> Parser::parse_font_feature_settings_value(TokenStrea
return StyleValueList::create(move(feature_tags), StyleValueList::Separator::Comma);
}
RefPtr<CSSStyleValue const> Parser::parse_font_style_value(TokenStream<ComponentValue>& tokens)
{
// https://drafts.csswg.org/css-fonts/#font-style-prop
// normal | italic | left | right | oblique <angle [-90deg,90deg]>?
auto transaction = tokens.begin_transaction();
auto keyword_value = parse_css_value_for_property(PropertyID::FontStyle, tokens);
if (!keyword_value)
return nullptr;
auto font_style = keyword_to_font_style(keyword_value->to_keyword());
VERIFY(font_style.has_value());
if (tokens.has_next_token() && keyword_value->to_keyword() == Keyword::Oblique) {
if (auto angle_value = parse_angle_value(tokens)) {
if (angle_value->is_angle()) {
auto angle = angle_value->as_angle().angle();
auto angle_degrees = angle.to_degrees();
if (angle_degrees < -90 || angle_degrees > 90)
return nullptr;
}
transaction.commit();
return FontStyleStyleValue::create(*font_style, angle_value);
}
}
transaction.commit();
return FontStyleStyleValue::create(*font_style);
}
RefPtr<CSSStyleValue const> Parser::parse_font_variation_settings_value(TokenStream<ComponentValue>& tokens)
{
// https://drafts.csswg.org/css-fonts/#propdef-font-variation-settings

View file

@ -1440,10 +1440,8 @@
"animation-type": "custom",
"inherited": true,
"initial": "normal",
"valid-identifiers": [
"italic",
"normal",
"oblique"
"valid-types": [
"font-style"
]
},
"font-variant": {

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2025, Tim Ledbetter <tim.ledbetter@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "FontStyleStyleValue.h"
#include <LibWeb/CSS/CSSStyleValue.h>
#include <LibWeb/CSS/Serialize.h>
#include <LibWeb/CSS/StyleValues/AngleStyleValue.h>
namespace Web::CSS {
FontStyleStyleValue::FontStyleStyleValue(FontStyle font_style, ValueComparingRefPtr<CSSStyleValue const> angle_value)
: StyleValueWithDefaultOperators(Type::FontStyle)
, m_font_style(font_style)
, m_angle_value(angle_value)
{
}
FontStyleStyleValue::~FontStyleStyleValue() = default;
String FontStyleStyleValue::to_string(SerializationMode mode) const
{
Optional<String> angle_string;
if (m_angle_value) {
angle_string = m_angle_value->to_string(mode);
if (m_font_style == FontStyle::Oblique && angle_string == "0deg"sv)
return "normal"_string;
}
StringBuilder builder;
builder.append(CSS::to_string(m_font_style));
// https://drafts.csswg.org/css-fonts/#valdef-font-style-oblique-angle--90deg-90deg
// The lack of an <angle> represents 14deg. (Note that a font might internally provide its own mapping for "oblique", but the mapping within the font is disregarded.)
static auto default_angle = Angle::make_degrees(14);
if (angle_string.has_value() && !(m_angle_value->is_angle() && m_angle_value->as_angle().angle() == default_angle))
builder.appendff(" {}", angle_string);
return MUST(builder.to_string());
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2025, Tim Ledbetter <tim.ledbetter@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/CSS/CSSStyleValue.h>
#include <LibWeb/CSS/Enums.h>
namespace Web::CSS {
class FontStyleStyleValue final : public StyleValueWithDefaultOperators<FontStyleStyleValue> {
public:
static ValueComparingNonnullRefPtr<FontStyleStyleValue const> create(FontStyle font_style, ValueComparingRefPtr<CSSStyleValue const> angle_value = {})
{
return adopt_ref(*new (nothrow) FontStyleStyleValue(font_style, angle_value));
}
virtual ~FontStyleStyleValue() override;
FontStyle font_style() const { return m_font_style; }
ValueComparingRefPtr<CSSStyleValue const> angle() const { return m_angle_value; }
virtual String to_string(SerializationMode) const override;
bool equals(CSSStyleValue const& other) const override
{
if (type() != other.type())
return false;
auto const& other_font_style = other.as_font_style();
return m_font_style == other_font_style.m_font_style && m_angle_value == other_font_style.m_angle_value;
}
bool properties_equal(FontStyleStyleValue const& other) const { return m_font_style == other.m_font_style && m_angle_value == other.m_angle_value; }
private:
FontStyleStyleValue(FontStyle, ValueComparingRefPtr<CSSStyleValue const> angle_value);
FontStyle m_font_style;
ValueComparingRefPtr<CSSStyleValue const> m_angle_value;
};
}

View file

@ -233,8 +233,9 @@ String ShorthandStyleValue::to_string(SerializationMode mode) const
builder.append(' ');
builder.append(string);
};
if (font_style->to_keyword() != Keyword::Normal && font_style->to_keyword() != Keyword::Initial)
append(font_style->to_string(mode));
auto font_style_string = font_style->to_string(mode);
if (font_style_string != "normal"sv)
append(font_style_string);
if (font_variant_string != "normal"sv && font_variant_string != "initial"sv)
append(font_variant_string);
if (font_weight->to_font_weight() != Gfx::FontWeight::Regular && font_weight->to_keyword() != Keyword::Initial)

View file

@ -204,6 +204,7 @@ class FlexStyleValue;
class FontFace;
class FontFaceSet;
class FontSourceStyleValue;
class FontStyleStyleValue;
class Frequency;
class FrequencyOrCalculated;
class FrequencyPercentage;

View file

@ -2,20 +2,20 @@ Harness status: OK
Found 15 tests
3 Pass
12 Fail
12 Pass
3 Fail
Pass Property font-style value 'normal'
Pass Property font-style value 'italic'
Pass Property font-style value 'oblique'
Fail Property font-style value 'oblique 0deg'
Fail Property font-style value 'oblique calc(10deg - 10deg)'
Fail Property font-style value 'oblique 10deg'
Fail Property font-style value 'oblique -10deg'
Fail Property font-style value 'oblique -90deg'
Fail Property font-style value 'oblique 90deg'
Fail Property font-style value 'oblique 10grad'
Fail Property font-style value 'oblique calc(90deg)'
Pass Property font-style value 'oblique 0deg'
Pass Property font-style value 'oblique calc(10deg - 10deg)'
Pass Property font-style value 'oblique 10deg'
Pass Property font-style value 'oblique -10deg'
Pass Property font-style value 'oblique -90deg'
Pass Property font-style value 'oblique 90deg'
Pass Property font-style value 'oblique 10grad'
Pass Property font-style value 'oblique calc(90deg)'
Fail Property font-style value 'oblique calc(100deg)'
Fail Property font-style value 'oblique calc(-100deg)'
Fail Property font-style value 'oblique calc(30deg * 2)'
Pass Property font-style value 'oblique calc(30deg * 2)'
Fail Property font-style value 'oblique calc(30deg + (sign(20cqw - 10px) * 5deg))'

View file

@ -2,20 +2,20 @@ Harness status: OK
Found 15 tests
3 Pass
12 Fail
14 Pass
1 Fail
Pass e.style['font-style'] = "normal" should set the property value
Pass e.style['font-style'] = "italic" should set the property value
Pass e.style['font-style'] = "oblique" should set the property value
Fail e.style['font-style'] = "oblique 0deg" should set the property value
Fail e.style['font-style'] = "oblique calc(10deg - 10deg)" should set the property value
Fail e.style['font-style'] = "oblique 10deg" should set the property value
Fail e.style['font-style'] = "oblique -10deg" should set the property value
Fail e.style['font-style'] = "oblique -90deg" should set the property value
Fail e.style['font-style'] = "oblique 90deg" should set the property value
Fail e.style['font-style'] = "oblique 10grad" should set the property value
Fail e.style['font-style'] = "oblique calc(90deg)" should set the property value
Fail e.style['font-style'] = "oblique calc(100deg)" should set the property value
Fail e.style['font-style'] = "oblique calc(-100deg)" should set the property value
Fail e.style['font-style'] = "oblique calc(30deg * 2)" should set the property value
Pass e.style['font-style'] = "oblique 0deg" should set the property value
Pass e.style['font-style'] = "oblique calc(10deg - 10deg)" should set the property value
Pass e.style['font-style'] = "oblique 10deg" should set the property value
Pass e.style['font-style'] = "oblique -10deg" should set the property value
Pass e.style['font-style'] = "oblique -90deg" should set the property value
Pass e.style['font-style'] = "oblique 90deg" should set the property value
Pass e.style['font-style'] = "oblique 10grad" should set the property value
Pass e.style['font-style'] = "oblique calc(90deg)" should set the property value
Pass e.style['font-style'] = "oblique calc(100deg)" should set the property value
Pass e.style['font-style'] = "oblique calc(-100deg)" should set the property value
Pass e.style['font-style'] = "oblique calc(30deg * 2)" should set the property value
Fail e.style['font-style'] = "oblique calc(30deg + (sign(2cqw - 10px) * 5deg))" should set the property value