diff --git a/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt b/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt index a67ee57e0e6..d1eb154fe65 100644 --- a/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt +++ b/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt @@ -31,6 +31,7 @@ math-style: normal pointer-events: auto quotes: auto stroke: none +stroke-linecap: butt stroke-opacity: 1 stroke-width: 1px tab-size: 8 @@ -120,7 +121,7 @@ grid-row-start: auto grid-template-areas: grid-template-columns: grid-template-rows: -height: 2074px +height: 2091px inline-size: auto inset-block-end: auto inset-block-start: auto diff --git a/Userland/Libraries/LibWeb/CSS/ComputedValues.h b/Userland/Libraries/LibWeb/CSS/ComputedValues.h index ad7e094730f..96e443b01d9 100644 --- a/Userland/Libraries/LibWeb/CSS/ComputedValues.h +++ b/Userland/Libraries/LibWeb/CSS/ComputedValues.h @@ -144,6 +144,7 @@ public: static float fill_opacity() { return 1.0f; } static CSS::FillRule fill_rule() { return CSS::FillRule::Nonzero; } static CSS::ClipRule clip_rule() { return CSS::ClipRule::Nonzero; } + static CSS::StrokeLinecap stroke_linecap() { return CSS::StrokeLinecap::Butt; } static float stroke_opacity() { return 1.0f; } static float stop_opacity() { return 1.0f; } static CSS::TextAnchor text_anchor() { return CSS::TextAnchor::Start; } @@ -464,6 +465,7 @@ public: CSS::FillRule fill_rule() const { return m_inherited.fill_rule; } Optional const& stroke() const { return m_inherited.stroke; } float fill_opacity() const { return m_inherited.fill_opacity; } + CSS::StrokeLinecap stroke_linecap() const { return m_inherited.stroke_linecap; } float stroke_opacity() const { return m_inherited.stroke_opacity; } LengthPercentage const& stroke_width() const { return m_inherited.stroke_width; } Color stop_color() const { return m_noninherited.stop_color; } @@ -555,6 +557,7 @@ protected: CSS::FillRule fill_rule { InitialValues::fill_rule() }; Optional stroke; float fill_opacity { InitialValues::fill_opacity() }; + CSS::StrokeLinecap stroke_linecap { InitialValues::stroke_linecap() }; float stroke_opacity { InitialValues::stroke_opacity() }; LengthPercentage stroke_width { Length::make_px(1) }; CSS::TextAnchor text_anchor { InitialValues::text_anchor() }; @@ -791,6 +794,7 @@ public: void set_stroke(SVGPaint value) { m_inherited.stroke = value; } void set_fill_rule(CSS::FillRule value) { m_inherited.fill_rule = value; } void set_fill_opacity(float value) { m_inherited.fill_opacity = value; } + void set_stroke_linecap(CSS::StrokeLinecap value) { m_inherited.stroke_linecap = value; } void set_stroke_opacity(float value) { m_inherited.stroke_opacity = value; } void set_stroke_width(LengthPercentage value) { m_inherited.stroke_width = value; } void set_stop_color(Color value) { m_noninherited.stop_color = value; } diff --git a/Userland/Libraries/LibWeb/CSS/Enums.json b/Userland/Libraries/LibWeb/CSS/Enums.json index 5df1b01fbf9..d5f04f7181a 100644 --- a/Userland/Libraries/LibWeb/CSS/Enums.json +++ b/Userland/Libraries/LibWeb/CSS/Enums.json @@ -417,6 +417,11 @@ "thin", "none" ], + "stroke-linecap": [ + "butt", + "square", + "round" + ], "text-align": [ "center", "justify", diff --git a/Userland/Libraries/LibWeb/CSS/Keywords.json b/Userland/Libraries/LibWeb/CSS/Keywords.json index a5b7208ad38..9f5f76da19f 100644 --- a/Userland/Libraries/LibWeb/CSS/Keywords.json +++ b/Userland/Libraries/LibWeb/CSS/Keywords.json @@ -90,6 +90,7 @@ "bottom", "break-word", "browser", + "butt", "button", "buttonborder", "buttonface", diff --git a/Userland/Libraries/LibWeb/CSS/Properties.json b/Userland/Libraries/LibWeb/CSS/Properties.json index 55891ea080e..89dfc40e757 100644 --- a/Userland/Libraries/LibWeb/CSS/Properties.json +++ b/Userland/Libraries/LibWeb/CSS/Properties.json @@ -2418,6 +2418,15 @@ "paint" ] }, + "stroke-linecap": { + "affects-layout": false, + "animation-type": "discrete", + "inherited": true, + "initial": "butt", + "valid-types": [ + "stroke-linecap" + ] + }, "stroke-opacity": { "affects-layout": false, "animation-type": "by-computed-value", diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp index 5fde4e031f4..5f435bf7db0 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp @@ -326,6 +326,12 @@ float StyleProperties::fill_opacity() const return resolve_opacity_value(*value); } +Optional StyleProperties::stroke_linecap() const +{ + auto value = property(CSS::PropertyID::StrokeLinecap); + return keyword_to_stroke_linecap(value->to_keyword()); +} + float StyleProperties::stroke_opacity() const { auto value = property(CSS::PropertyID::StrokeOpacity); diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.h b/Userland/Libraries/LibWeb/CSS/StyleProperties.h index bbdb5884b38..68438564141 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.h +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.h @@ -177,6 +177,7 @@ public: Color stop_color() const; float stop_opacity() const; float fill_opacity() const; + Optional stroke_linecap() const; float stroke_opacity() const; Optional fill_rule() const; Optional clip_rule() const; diff --git a/Userland/Libraries/LibWeb/Layout/Node.cpp b/Userland/Libraries/LibWeb/Layout/Node.cpp index 48cf9bba87d..5110892e8a5 100644 --- a/Userland/Libraries/LibWeb/Layout/Node.cpp +++ b/Userland/Libraries/LibWeb/Layout/Node.cpp @@ -840,6 +840,8 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style) computed_values.set_fill_rule(*fill_rule); computed_values.set_fill_opacity(computed_style.fill_opacity()); + if (auto stroke_linecap = computed_style.stroke_linecap(); stroke_linecap.has_value()) + computed_values.set_stroke_linecap(stroke_linecap.value()); computed_values.set_stroke_opacity(computed_style.stroke_opacity()); computed_values.set_stop_opacity(computed_style.stop_opacity()); diff --git a/Userland/Libraries/LibWeb/Painting/SVGPathPaintable.cpp b/Userland/Libraries/LibWeb/Painting/SVGPathPaintable.cpp index 68d50a1c8db..eb7a407329d 100644 --- a/Userland/Libraries/LibWeb/Painting/SVGPathPaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/SVGPathPaintable.cpp @@ -132,6 +132,9 @@ void SVGPathPaintable::paint(PaintContext& context, PaintPhase phase) const }); } + auto stroke_linecap = graphics_element.stroke_linecap().value_or(CSS::StrokeLinecap::Butt); + (void)stroke_linecap; // FIXME: Use + auto stroke_opacity = graphics_element.stroke_opacity().value_or(1); // Note: This is assuming .x_scale() == .y_scale() (which it does currently). diff --git a/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp index fcdb24e8958..77c8351e88f 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp @@ -234,6 +234,13 @@ Optional SVGGraphicsElement::fill_opacity() const return layout_node()->computed_values().fill_opacity(); } +Optional SVGGraphicsElement::stroke_linecap() const +{ + if (!layout_node()) + return {}; + return layout_node()->computed_values().stroke_linecap(); +} + Optional SVGGraphicsElement::stroke_opacity() const { if (!layout_node()) diff --git a/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h index b9c9ed9b2a5..198188e19f2 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h +++ b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h @@ -38,6 +38,7 @@ public: Optional stroke_color() const; Optional stroke_width() const; Optional fill_opacity() const; + Optional stroke_linecap() const; Optional stroke_opacity() const; Optional fill_rule() const; Optional clip_rule() const;