diff --git a/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp index 4219f449dc..de3e4c493d 100644 --- a/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp @@ -2,6 +2,7 @@ * Copyright (c) 2021, Andreas Kling * Copyright (c) 2022, Sam Atkins * Copyright (c) 2022, Tobias Christiansen + * Copyright (c) 2023, MacDue * * SPDX-License-Identifier: BSD-2-Clause */ @@ -37,7 +38,8 @@ void SVGFormattingContext::run(Box const& box, LayoutMode, [[maybe_unused]] Avai auto& svg_svg_element = verify_cast(*box.dom_node()); - auto root_offset = m_state.get(box).offset; + auto svg_box_state = m_state.get(box); + auto root_offset = svg_box_state.offset; box.for_each_child_of_type([&](BlockContainer const& child_box) { if (is(child_box.dom_node())) { @@ -53,48 +55,41 @@ void SVGFormattingContext::run(Box const& box, LayoutMode, [[maybe_unused]] Avai box.for_each_in_subtree_of_type([&](SVGBox const& descendant) { if (is(descendant)) { auto const& geometry_box = static_cast(descendant); - auto& geometry_box_state = m_state.get_mutable(geometry_box); - auto& dom_node = const_cast(geometry_box).dom_node(); - auto& svg_svg_state = m_state.get(static_cast(*svg_svg_element.layout_node())); - - if (svg_svg_state.has_definite_width() && svg_svg_state.has_definite_height()) { - geometry_box_state.set_content_offset({ 0, 0 }); - geometry_box_state.set_content_width(svg_svg_state.content_width()); - geometry_box_state.set_content_height(svg_svg_state.content_height()); - return IterationDecision::Continue; - } - - // FIXME: Allow for one of {width, height} to not be specified} - if (svg_svg_element.has_attribute(HTML::AttributeNames::width)) { - } - - if (svg_svg_element.has_attribute(HTML::AttributeNames::height)) { - } - auto& path = dom_node.get_path(); - auto path_bounding_box = path.bounding_box().to_type(); - - // Stroke increases the path's size by stroke_width/2 per side. - CSSPixels stroke_width = geometry_box.dom_node().stroke_width().value_or(0); - path_bounding_box.inflate(stroke_width, stroke_width); + auto transform = dom_node.get_transform(); auto& maybe_view_box = svg_svg_element.view_box(); + float viewbox_scale = 1.0f; + CSSPixelPoint offset {}; if (maybe_view_box.has_value()) { auto view_box = maybe_view_box.value(); - CSSPixelPoint viewbox_offset = { view_box.min_x, view_box.min_y }; - geometry_box_state.set_content_offset(path_bounding_box.top_left() + viewbox_offset); + // FIXME: This should allow just one of width or height to be specified. + // E.g. We should be able to layout where height is unspecified/auto. + if (!svg_box_state.has_definite_width() || !svg_box_state.has_definite_height()) { + dbgln("FIXME: Attempting to layout indefinitely sized SVG with a viewbox -- this likely won't work!"); + } + auto scale_width = svg_box_state.has_definite_width() ? svg_box_state.content_width().value() / view_box.width : 1; + auto scale_height = svg_box_state.has_definite_height() ? svg_box_state.content_height().value() / view_box.height : 1; + viewbox_scale = min(scale_width, scale_height); - geometry_box_state.set_content_width(view_box.width); - geometry_box_state.set_content_height(view_box.height); + // Center the viewbox within the SVG element: + if (svg_box_state.has_definite_width()) + offset.translate_by((svg_box_state.content_width() - (view_box.width * viewbox_scale)) / 2, 0); + if (svg_box_state.has_definite_height()) + offset.translate_by(0, (svg_box_state.content_height() - (view_box.height * viewbox_scale)) / 2); - return IterationDecision::Continue; + transform = Gfx::AffineTransform {}.scale(viewbox_scale, viewbox_scale).translate({ -view_box.min_x, -view_box.min_y }).multiply(transform); } - geometry_box_state.set_content_offset(path_bounding_box.top_left()); + // Stroke increases the path's size by stroke_width/2 per side. + auto path_bounding_box = transform.map(path.bounding_box()).to_type(); + CSSPixels stroke_width = geometry_box.dom_node().stroke_width().value_or(0); + path_bounding_box.inflate(stroke_width, stroke_width); + geometry_box_state.set_content_offset(path_bounding_box.top_left() + offset); geometry_box_state.set_content_width(path_bounding_box.width()); geometry_box_state.set_content_height(path_bounding_box.height()); }