mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 21:22:46 +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
					
				
					 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 MacDue
						MacDue