From 17a6fcfde339bede45ef61913c28d41dbc77da09 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 11 Jul 2022 12:25:01 +0200 Subject: [PATCH] LibWeb: Try to work out the intrinsic size of elements If the `width` and `height` attributes are provided, we derive the intrinsic size and ratio from them. Otherwise, we trace a rectangle around the geometry elements inside the SVG and use the size of that as the intrinsic size. This is definitely far from correct, but is still a much better guess at the intrinsic size than nothing. --- .../Libraries/LibWeb/Layout/SVGSVGBox.cpp | 71 +++++++++++++++++++ Userland/Libraries/LibWeb/Layout/SVGSVGBox.h | 2 + 2 files changed, 73 insertions(+) diff --git a/Userland/Libraries/LibWeb/Layout/SVGSVGBox.cpp b/Userland/Libraries/LibWeb/Layout/SVGSVGBox.cpp index f521e0da39..5b7398892b 100644 --- a/Userland/Libraries/LibWeb/Layout/SVGSVGBox.cpp +++ b/Userland/Libraries/LibWeb/Layout/SVGSVGBox.cpp @@ -1,10 +1,13 @@ /* * Copyright (c) 2020, Matthew Olsson + * Copyright (c) 2022, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ +#include #include +#include #include namespace Web::Layout { @@ -19,4 +22,72 @@ RefPtr SVGSVGBox::create_paintable() const return Painting::SVGSVGPaintable::create(*this); } +void SVGSVGBox::prepare_for_replaced_layout() +{ + if (dom_node().has_attribute(HTML::AttributeNames::width) && dom_node().has_attribute(HTML::AttributeNames::height)) { + Optional w; + Optional h; + if (auto width = HTML::parse_dimension_value(dom_node().attribute(HTML::AttributeNames::width))) { + if (width->has_length()) + w = width->to_length().to_px(*this); + } + if (auto height = HTML::parse_dimension_value(dom_node().attribute(HTML::AttributeNames::height))) { + if (height->has_length()) + h = height->to_length().to_px(*this); + } + if (w.has_value() && h.has_value()) { + set_intrinsic_width(*w); + set_intrinsic_height(*h); + set_intrinsic_aspect_ratio(*w / *h); + return; + } + } + + Optional united_rect; + + auto add_to_united_rect = [&](Gfx::FloatRect const& rect) { + if (united_rect.has_value()) + united_rect = united_rect->united(rect); + else + united_rect = rect; + }; + + for_each_in_subtree_of_type([&](SVGGeometryBox const& geometry_box) { + auto& dom_node = const_cast(geometry_box.dom_node()); + if (dom_node.has_attribute(HTML::AttributeNames::width) && dom_node.has_attribute(HTML::AttributeNames::height)) { + Gfx::FloatRect rect; + // FIXME: Allow for relative lengths here + rect.set_width(computed_values().width().resolved(*this, CSS::Length::make_px(0)).to_px(*this)); + rect.set_height(computed_values().height().resolved(*this, CSS::Length::make_px(0)).to_px(*this)); + add_to_united_rect(rect); + return IterationDecision::Continue; + } + + auto& path = dom_node.get_path(); + auto path_bounding_box = path.bounding_box(); + + // Stroke increases the path's size by stroke_width/2 per side. + auto stroke_width = geometry_box.dom_node().stroke_width().value_or(0); + path_bounding_box.inflate(stroke_width, stroke_width); + + auto& maybe_view_box = this->dom_node().view_box(); + + if (maybe_view_box.has_value()) { + auto view_box = maybe_view_box.value(); + Gfx::FloatRect rect(view_box.min_x, view_box.min_y, view_box.width, view_box.height); + add_to_united_rect(rect); + return IterationDecision::Continue; + } + + add_to_united_rect(path_bounding_box); + return IterationDecision::Continue; + }); + + if (united_rect.has_value()) { + set_intrinsic_width(united_rect->width()); + set_intrinsic_height(united_rect->height()); + set_intrinsic_aspect_ratio(united_rect->width() / united_rect->height()); + } +} + } diff --git a/Userland/Libraries/LibWeb/Layout/SVGSVGBox.h b/Userland/Libraries/LibWeb/Layout/SVGSVGBox.h index 530fa27856..55d4967c41 100644 --- a/Userland/Libraries/LibWeb/Layout/SVGSVGBox.h +++ b/Userland/Libraries/LibWeb/Layout/SVGSVGBox.h @@ -21,6 +21,8 @@ public: virtual bool can_have_children() const override { return true; } virtual RefPtr create_paintable() const override; + + virtual void prepare_for_replaced_layout() override; }; }