mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-26 17:40:27 +00:00
LibWeb: Support x
and y
attributes on nested SVGs
This allows positioning a child SVG relative to its parent SVG. Note: These have been implemented as CSS properties as in SVG 2, these are geometry properties that can be used in CSS (see https://www.w3.org/TR/SVG/geometry.html), but there is not much browser support for this. It is nicer to implement than the ad-hoc SVG attribute parsing though, so I feel it may make sense to port the rest of the attributes specified here (which should fix some issues with viewport relative sizes).
This commit is contained in:
parent
556679fedd
commit
b10f58a1fe
Notes:
sideshowbarker
2024-07-17 00:47:29 +09:00
Author: https://github.com/MacDue Commit: https://github.com/SerenityOS/serenity/commit/b10f58a1fe Pull-request: https://github.com/SerenityOS/serenity/pull/22975 Reviewed-by: https://github.com/AtkinsSJ
8 changed files with 91 additions and 2 deletions
26
Tests/LibWeb/Layout/expected/svg/svg-inside-svg-with-xy.txt
Normal file
26
Tests/LibWeb/Layout/expected/svg/svg-inside-svg-with-xy.txt
Normal file
|
@ -0,0 +1,26 @@
|
|||
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
|
||||
BlockContainer <html> at (0,0) content-size 800x600 [BFC] children: not-inline
|
||||
BlockContainer <body> at (8,8) content-size 784x150 children: inline
|
||||
frag 0 from SVGSVGBox start: 0, length: 0, rect: [8,8 300x150] baseline: 150
|
||||
SVGSVGBox <svg> at (8,8) content-size 300x150 [SVG] children: inline
|
||||
TextNode <#text>
|
||||
SVGSVGBox <svg> at (18,8) content-size 300x150 [SVG] children: inline
|
||||
TextNode <#text>
|
||||
SVGGeometryBox <rect> at (27.5,17.5) content-size 101x101 children: not-inline
|
||||
TextNode <#text>
|
||||
TextNode <#text>
|
||||
SVGSVGBox <svg> at (208,23) content-size 300x150 [SVG] children: inline
|
||||
TextNode <#text>
|
||||
SVGGeometryBox <rect> at (217.5,32.5) content-size 101x101 children: not-inline
|
||||
TextNode <#text>
|
||||
TextNode <#text>
|
||||
TextNode <#text>
|
||||
|
||||
ViewportPaintable (Viewport<#document>) [0,0 800x600]
|
||||
PaintableWithLines (BlockContainer<HTML>) [0,0 800x600]
|
||||
PaintableWithLines (BlockContainer<BODY>) [8,8 784x150]
|
||||
SVGSVGPaintable (SVGSVGBox<svg>) [8,8 300x150]
|
||||
SVGSVGPaintable (SVGSVGBox<svg>) [18,8 300x150]
|
||||
SVGPathPaintable (SVGGeometryBox<rect>) [27.5,17.5 101x101]
|
||||
SVGSVGPaintable (SVGSVGBox<svg>) [208,23 300x150]
|
||||
SVGPathPaintable (SVGGeometryBox<rect>) [217.5,32.5 101x101]
|
11
Tests/LibWeb/Layout/input/svg/svg-inside-svg-with-xy.html
Normal file
11
Tests/LibWeb/Layout/input/svg/svg-inside-svg-with-xy.html
Normal file
|
@ -0,0 +1,11 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<svg x="10">
|
||||
<rect x="10" y="10" height="100" width="100"
|
||||
style="stroke:#ff0000; fill: #0000ff"/>
|
||||
</svg>
|
||||
<svg x="200" y="15">
|
||||
<rect x="10" y="10" height="100" width="100"
|
||||
style="stroke:#009900; fill: #00cc00"/>
|
||||
</svg>
|
||||
</svg>
|
After Width: | Height: | Size: 343 B |
|
@ -166,4 +166,6 @@ white-space: normal
|
|||
width: auto
|
||||
word-spacing: normal
|
||||
word-wrap: normal
|
||||
x: 0px
|
||||
y: 0px
|
||||
z-index: auto
|
||||
|
|
|
@ -161,6 +161,8 @@ public:
|
|||
static CSS::TableLayout table_layout() { return CSS::TableLayout::Auto; }
|
||||
static QuotesData quotes() { return QuotesData { .type = QuotesData::Type::Auto }; }
|
||||
static CSS::TransformBox transform_box() { return CSS::TransformBox::ViewBox; }
|
||||
static LengthPercentage x() { return CSS::Length::make_px(0); }
|
||||
static LengthPercentage y() { return CSS::Length::make_px(0); }
|
||||
|
||||
static CSS::MaskType mask_type() { return CSS::MaskType::Luminance; }
|
||||
static CSS::MathShift math_shift() { return CSS::MathShift::Normal; }
|
||||
|
@ -392,6 +394,8 @@ public:
|
|||
CSS::TextAnchor text_anchor() const { return m_inherited.text_anchor; }
|
||||
Optional<MaskReference> const& mask() const { return m_noninherited.mask; }
|
||||
CSS::MaskType mask_type() const { return m_noninherited.mask_type; }
|
||||
LengthPercentage const& x() const { return m_noninherited.x; }
|
||||
LengthPercentage const& y() const { return m_noninherited.y; }
|
||||
|
||||
Vector<CSS::Transformation> const& transformations() const { return m_noninherited.transformations; }
|
||||
CSS::TransformBox const& transform_box() const { return m_noninherited.transform_box; }
|
||||
|
@ -545,6 +549,8 @@ protected:
|
|||
|
||||
Optional<MaskReference> mask;
|
||||
CSS::MaskType mask_type { InitialValues::mask_type() };
|
||||
LengthPercentage x { InitialValues::x() };
|
||||
LengthPercentage y { InitialValues::x() };
|
||||
} m_noninherited;
|
||||
};
|
||||
|
||||
|
@ -667,6 +673,8 @@ public:
|
|||
void set_outline_width(CSS::Length value) { m_noninherited.outline_width = value; }
|
||||
void set_mask(MaskReference value) { m_noninherited.mask = value; }
|
||||
void set_mask_type(CSS::MaskType value) { m_noninherited.mask_type = value; }
|
||||
void set_x(LengthPercentage x) { m_noninherited.x = x; }
|
||||
void set_y(LengthPercentage y) { m_noninherited.y = y; }
|
||||
|
||||
void set_math_shift(CSS::MathShift value) { m_inherited.math_shift = value; }
|
||||
void set_math_style(CSS::MathStyle value) { m_inherited.math_style = value; }
|
||||
|
|
|
@ -2196,6 +2196,32 @@
|
|||
"normal"
|
||||
]
|
||||
},
|
||||
"x": {
|
||||
"__comment": "This is an SVG 2 geometry property, see: https://www.w3.org/TR/SVG/geometry.html#X.",
|
||||
"inherited": false,
|
||||
"initial": "0",
|
||||
"valid-types": [
|
||||
"length [-∞,∞]",
|
||||
"percentage [-∞,∞]"
|
||||
],
|
||||
"percentages-resolve-to": "length",
|
||||
"quirks": [
|
||||
"unitless-length"
|
||||
]
|
||||
},
|
||||
"y": {
|
||||
"__comment": "This is an SVG 2 geometry property, see: https://www.w3.org/TR/SVG/geometry.html#Y.",
|
||||
"inherited": false,
|
||||
"initial": "0",
|
||||
"valid-types": [
|
||||
"length [-∞,∞]",
|
||||
"percentage [-∞,∞]"
|
||||
],
|
||||
"percentages-resolve-to": "length",
|
||||
"quirks": [
|
||||
"unitless-length"
|
||||
]
|
||||
},
|
||||
"z-index": {
|
||||
"affects-layout": false,
|
||||
"affects-stacking-context": true,
|
||||
|
|
|
@ -751,6 +751,10 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
|
|||
computed_values.set_grid_template_areas(computed_style.grid_template_areas());
|
||||
computed_values.set_grid_auto_flow(computed_style.grid_auto_flow());
|
||||
|
||||
if (auto x_value = computed_style.length_percentage(CSS::PropertyID::X); x_value.has_value())
|
||||
computed_values.set_x(*x_value);
|
||||
if (auto y_value = computed_style.length_percentage(CSS::PropertyID::Y); y_value.has_value())
|
||||
computed_values.set_y(*y_value);
|
||||
auto fill = computed_style.property(CSS::PropertyID::Fill);
|
||||
if (fill->has_color())
|
||||
computed_values.set_fill(fill->to_color(*this));
|
||||
|
|
|
@ -285,9 +285,11 @@ void SVGFormattingContext::run(Box const& box, LayoutMode layout_mode, Available
|
|||
return size.to_px(node, reference_value);
|
||||
};
|
||||
|
||||
// FIXME: Support the x/y attributes to calculate the offset.
|
||||
auto nested_viewport_x = descendant.computed_values().x().to_px(descendant, viewport_width);
|
||||
auto nested_viewport_y = descendant.computed_values().y().to_px(descendant, viewport_height);
|
||||
auto nested_viewport_width = resolve_dimension(descendant, descendant.computed_values().width(), viewport_width);
|
||||
auto nested_viewport_height = resolve_dimension(descendant, descendant.computed_values().height(), viewport_height);
|
||||
nested_viewport_state.set_content_offset({ nested_viewport_x, nested_viewport_y });
|
||||
nested_viewport_state.set_content_width(nested_viewport_width);
|
||||
nested_viewport_state.set_content_height(nested_viewport_height);
|
||||
nested_context.run(static_cast<Box const&>(descendant), layout_mode, available_space);
|
||||
|
|
|
@ -47,9 +47,19 @@ JS::GCPtr<Layout::Node> SVGSVGElement::create_layout_node(NonnullRefPtr<CSS::Sty
|
|||
void SVGSVGElement::apply_presentational_hints(CSS::StyleProperties& style) const
|
||||
{
|
||||
Base::apply_presentational_hints(style);
|
||||
auto parsing_context = CSS::Parser::ParsingContext { document(), CSS::Parser::ParsingContext::Mode::SVGPresentationAttribute };
|
||||
|
||||
auto x_attribute = attribute(SVG::AttributeNames::x);
|
||||
if (auto x_value = parse_css_value(parsing_context, x_attribute.value_or(String {}), CSS::PropertyID::X)) {
|
||||
style.set_property(CSS::PropertyID::X, x_value.release_nonnull());
|
||||
}
|
||||
|
||||
auto y_attribute = attribute(SVG::AttributeNames::y);
|
||||
if (auto y_value = parse_css_value(parsing_context, y_attribute.value_or(String {}), CSS::PropertyID::Y)) {
|
||||
style.set_property(CSS::PropertyID::Y, y_value.release_nonnull());
|
||||
}
|
||||
|
||||
auto width_attribute = attribute(SVG::AttributeNames::width);
|
||||
auto parsing_context = CSS::Parser::ParsingContext { document(), CSS::Parser::ParsingContext::Mode::SVGPresentationAttribute };
|
||||
if (auto width_value = parse_css_value(parsing_context, width_attribute.value_or(String {}), CSS::PropertyID::Width)) {
|
||||
style.set_property(CSS::PropertyID::Width, width_value.release_nonnull());
|
||||
} else if (width_attribute == "") {
|
||||
|
|
Loading…
Reference in a new issue