mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-21 16:58:58 +00:00
LibWeb: Move common SVGViewport code into an SVGFitToViewBox class
Special handling for SVGClipPathElement and SVGMaskElement, which use a a ViewBox and PreserveAspectRatio value internally, has been moved to `SVGFormattingContext`.
This commit is contained in:
parent
c8d24d4966
commit
ea41aba6c7
Notes:
github-actions[bot]
2025-08-28 13:44:09 +00:00
Author: https://github.com/tcl3
Commit: ea41aba6c7
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6008
Reviewed-by: https://github.com/gmta ✅
16 changed files with 144 additions and 152 deletions
|
@ -878,6 +878,7 @@ set(SOURCES
|
|||
SVG/SVGFEMergeNodeElement.cpp
|
||||
SVG/SVGFEOffsetElement.cpp
|
||||
SVG/SVGFilterElement.cpp
|
||||
SVG/SVGFitToViewBox.cpp
|
||||
SVG/SVGForeignObjectElement.cpp
|
||||
SVG/SVGGElement.cpp
|
||||
SVG/SVGGeometryElement.cpp
|
||||
|
|
|
@ -1052,6 +1052,7 @@ class SVGFEBlendElement;
|
|||
class SVGFEFloodElement;
|
||||
class SVGFEGaussianBlurElement;
|
||||
class SVGFilterElement;
|
||||
class SVGFitToViewBox;
|
||||
class SVGForeignObjectElement;
|
||||
class SVGGeometryElement;
|
||||
class SVGGraphicsElement;
|
||||
|
|
|
@ -177,7 +177,6 @@ void SVGFormattingContext::run(AvailableSpace const& available_space)
|
|||
// NOTE: SVG doesn't have a "formatting context" in the spec, but this is the most
|
||||
// obvious way to drive SVG layout in our engine at the moment.
|
||||
|
||||
auto const& svg_viewport = as<SVG::SVGViewport>(*context_box().dom_node());
|
||||
auto& svg_box_state = m_state.get_mutable(context_box());
|
||||
|
||||
if (!this->context_box().root().document().is_decoded_svg()) {
|
||||
|
@ -197,38 +196,48 @@ void SVGFormattingContext::run(AvailableSpace const& available_space)
|
|||
svg_box_state.set_has_definite_width(true);
|
||||
svg_box_state.set_has_definite_height(true);
|
||||
|
||||
auto viewbox = svg_viewport.view_box();
|
||||
auto* dom_node = context_box().dom_node();
|
||||
VERIFY(dom_node);
|
||||
auto& svg_graphics_element = as<SVG::SVGGraphicsElement>(*dom_node);
|
||||
auto active_view_box = svg_graphics_element.active_view_box();
|
||||
// https://svgwg.org/svg2-draft/coords.html#ViewBoxAttribute
|
||||
if (viewbox.has_value()) {
|
||||
if (viewbox->width < 0 || viewbox->height < 0) {
|
||||
if (active_view_box.has_value()) {
|
||||
if (active_view_box->width < 0 || active_view_box->height < 0) {
|
||||
// A negative value for <width> or <height> is an error and invalidates the ‘viewBox’ attribute.
|
||||
viewbox = {};
|
||||
} else if (viewbox->width == 0 || viewbox->height == 0) {
|
||||
active_view_box = {};
|
||||
} else if (active_view_box->width == 0 || active_view_box->height == 0) {
|
||||
// A value of zero disables rendering of the element.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_current_viewbox_transform = m_parent_viewbox_transform;
|
||||
if (viewbox.has_value()) {
|
||||
if (active_view_box.has_value()) {
|
||||
// FIXME: This should allow just one of width or height to be specified.
|
||||
// E.g. We should be able to layout <svg width="100%"> where height is unspecified/auto.
|
||||
if (!svg_box_state.has_definite_width() || !svg_box_state.has_definite_height()) {
|
||||
dbgln_if(LIBWEB_CSS_DEBUG, "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() / viewbox->width : 1;
|
||||
auto scale_height = svg_box_state.has_definite_height() ? svg_box_state.content_height() / viewbox->height : 1;
|
||||
auto scale_width = svg_box_state.has_definite_width() ? svg_box_state.content_width() / active_view_box->width : 1;
|
||||
auto scale_height = svg_box_state.has_definite_height() ? svg_box_state.content_height() / active_view_box->height : 1;
|
||||
|
||||
// The initial value for preserveAspectRatio is xMidYMid meet.
|
||||
auto preserve_aspect_ratio = svg_viewport.preserve_aspect_ratio().value_or(SVG::PreserveAspectRatio {});
|
||||
auto viewbox_offset_and_scale = scale_and_align_viewbox_content(preserve_aspect_ratio, *viewbox, { scale_width, scale_height }, svg_box_state);
|
||||
SVG::PreserveAspectRatio preserve_aspect_ratio {};
|
||||
if (auto* view_box = as_if<SVG::SVGFitToViewBox>(*dom_node)) {
|
||||
preserve_aspect_ratio = view_box->preserve_aspect_ratio().value_or(SVG::PreserveAspectRatio {});
|
||||
} else if (is<SVG::SVGMaskElement>(*dom_node) || is<SVG::SVGClipPathElement>(*dom_node)) {
|
||||
// This allows mask and clipPath elements to be scaled in the x and y directions independently to match the target size.
|
||||
preserve_aspect_ratio = SVG::PreserveAspectRatio { SVG::PreserveAspectRatio::Align::None, {} };
|
||||
}
|
||||
|
||||
auto viewbox_offset_and_scale = scale_and_align_viewbox_content(preserve_aspect_ratio, *active_view_box, { scale_width, scale_height }, svg_box_state);
|
||||
|
||||
CSSPixelPoint offset = viewbox_offset_and_scale.offset;
|
||||
m_current_viewbox_transform = Gfx::AffineTransform { m_current_viewbox_transform }.multiply(Gfx::AffineTransform {}
|
||||
.translate(offset.to_type<float>())
|
||||
.scale(viewbox_offset_and_scale.scale_factor_x, viewbox_offset_and_scale.scale_factor_y)
|
||||
.translate({ -viewbox->min_x, -viewbox->min_y }));
|
||||
.translate({ -active_view_box->min_x, -active_view_box->min_y }));
|
||||
}
|
||||
|
||||
if (svg_box_state.has_definite_width() && svg_box_state.has_definite_height()) {
|
||||
|
@ -246,8 +255,8 @@ void SVGFormattingContext::run(AvailableSpace const& available_space)
|
|||
}
|
||||
|
||||
auto viewport_width = [&] {
|
||||
if (viewbox.has_value())
|
||||
return CSSPixels::nearest_value_for(viewbox->width);
|
||||
if (active_view_box.has_value())
|
||||
return CSSPixels::nearest_value_for(active_view_box->width);
|
||||
if (svg_box_state.has_definite_width())
|
||||
return svg_box_state.content_width();
|
||||
dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Failed to resolve width of SVG viewport!");
|
||||
|
@ -255,8 +264,8 @@ void SVGFormattingContext::run(AvailableSpace const& available_space)
|
|||
}();
|
||||
|
||||
auto viewport_height = [&] {
|
||||
if (viewbox.has_value())
|
||||
return CSSPixels::nearest_value_for(viewbox->height);
|
||||
if (active_view_box.has_value())
|
||||
return CSSPixels::nearest_value_for(active_view_box->height);
|
||||
if (svg_box_state.has_definite_height())
|
||||
return svg_box_state.content_height();
|
||||
dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Failed to resolve height of SVG viewport!");
|
||||
|
@ -275,7 +284,7 @@ void SVGFormattingContext::run(AvailableSpace const& available_space)
|
|||
|
||||
void SVGFormattingContext::layout_svg_element(Box const& child)
|
||||
{
|
||||
if (is<SVG::SVGViewport>(child.dom_node())) {
|
||||
if (is<SVG::SVGFitToViewBox>(child.dom_node())) {
|
||||
layout_nested_viewport(child);
|
||||
} else if (is<SVG::SVGForeignObjectElement>(child.dom_node()) && is<BlockContainer>(child)) {
|
||||
Layout::BlockFormattingContext bfc(m_state, m_layout_mode, static_cast<BlockContainer const&>(child), this);
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <LibWeb/SVG/AttributeNames.h>
|
||||
#include <LibWeb/SVG/AttributeParser.h>
|
||||
#include <LibWeb/SVG/SVGCircleElement.h>
|
||||
#include <LibWeb/SVG/SVGViewport.h>
|
||||
|
||||
namespace Web::SVG {
|
||||
|
||||
|
@ -58,6 +57,13 @@ void SVGCircleElement::apply_presentational_hints(GC::Ref<CSS::CascadedPropertie
|
|||
cascaded_properties->set_property_from_presentational_hint(CSS::PropertyID::R, r_value.release_nonnull());
|
||||
}
|
||||
|
||||
static CSSPixels normalized_diagonal_length(CSSPixelSize viewport_size)
|
||||
{
|
||||
if (viewport_size.width() == viewport_size.height())
|
||||
return viewport_size.width();
|
||||
return sqrt((viewport_size.width() * viewport_size.width()) + (viewport_size.height() * viewport_size.height())) / CSSPixels::nearest_value_for(AK::Sqrt2<float>);
|
||||
}
|
||||
|
||||
Gfx::Path SVGCircleElement::get_path(CSSPixelSize viewport_size)
|
||||
{
|
||||
auto node = layout_node();
|
||||
|
|
|
@ -9,19 +9,17 @@
|
|||
#include <LibWeb/SVG/AttributeParser.h>
|
||||
#include <LibWeb/SVG/SVGElement.h>
|
||||
#include <LibWeb/SVG/SVGGraphicsElement.h>
|
||||
#include <LibWeb/SVG/SVGViewport.h>
|
||||
|
||||
namespace Web::SVG {
|
||||
|
||||
class SVGClipPathElement final : public SVGGraphicsElement
|
||||
, public SVGViewport {
|
||||
class SVGClipPathElement final : public SVGGraphicsElement {
|
||||
WEB_PLATFORM_OBJECT(SVGClipPathElement, SVGGraphicsElement);
|
||||
GC_DECLARE_ALLOCATOR(SVGClipPathElement);
|
||||
|
||||
public:
|
||||
virtual ~SVGClipPathElement();
|
||||
|
||||
virtual Optional<ViewBox> view_box() const override
|
||||
virtual Optional<ViewBox> active_view_box() const override
|
||||
{
|
||||
// Same trick as SVGMaskElement.
|
||||
if (clip_path_units() == MaskContentUnits::ObjectBoundingBox)
|
||||
|
@ -29,11 +27,6 @@ public:
|
|||
return {};
|
||||
}
|
||||
|
||||
virtual Optional<PreserveAspectRatio> preserve_aspect_ratio() const override
|
||||
{
|
||||
return PreserveAspectRatio { PreserveAspectRatio::Align::None, {} };
|
||||
}
|
||||
|
||||
virtual void attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) override;
|
||||
|
||||
ClipPathUnits clip_path_units() const
|
||||
|
|
43
Libraries/LibWeb/SVG/SVGFitToViewBox.cpp
Normal file
43
Libraries/LibWeb/SVG/SVGFitToViewBox.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Tim Ledbetter <tim.ledbetter@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/Realm.h>
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
#include <LibWeb/SVG/AttributeNames.h>
|
||||
#include <LibWeb/SVG/SVGAnimatedRect.h>
|
||||
#include <LibWeb/SVG/SVGFitToViewBox.h>
|
||||
|
||||
namespace Web::SVG {
|
||||
|
||||
void SVGFitToViewBox::initialize(JS::Realm& realm)
|
||||
{
|
||||
m_view_box_for_bindings = realm.create<SVGAnimatedRect>(realm);
|
||||
}
|
||||
|
||||
void SVGFitToViewBox::visit_edges(JS::Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(m_view_box_for_bindings);
|
||||
}
|
||||
|
||||
void SVGFitToViewBox::attribute_changed(DOM::Element&, FlyString const& name, Optional<String> const& value)
|
||||
{
|
||||
if (name.equals_ignoring_ascii_case(SVG::AttributeNames::viewBox)) {
|
||||
if (!value.has_value()) {
|
||||
m_view_box_for_bindings->set_nulled(true);
|
||||
} else {
|
||||
m_view_box = try_parse_view_box(value.value_or(String {}));
|
||||
m_view_box_for_bindings->set_nulled(!m_view_box.has_value());
|
||||
if (m_view_box.has_value()) {
|
||||
m_view_box_for_bindings->set_base_val(Gfx::DoubleRect { m_view_box->min_x, m_view_box->min_y, m_view_box->width, m_view_box->height });
|
||||
m_view_box_for_bindings->set_anim_val(Gfx::DoubleRect { m_view_box->min_x, m_view_box->min_y, m_view_box->width, m_view_box->height });
|
||||
}
|
||||
}
|
||||
} else if (name.equals_ignoring_ascii_case(SVG::AttributeNames::preserveAspectRatio)) {
|
||||
m_preserve_aspect_ratio = AttributeParser::parse_preserve_aspect_ratio(value.value_or(String {}));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
35
Libraries/LibWeb/SVG/SVGFitToViewBox.h
Normal file
35
Libraries/LibWeb/SVG/SVGFitToViewBox.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Tim Ledbetter <tim.ledbetter@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Heap/Cell.h>
|
||||
#include <LibWeb/SVG/AttributeParser.h>
|
||||
#include <LibWeb/SVG/SVGAnimatedString.h>
|
||||
#include <LibWeb/SVG/ViewBox.h>
|
||||
|
||||
namespace Web::SVG {
|
||||
|
||||
class SVGFitToViewBox {
|
||||
public:
|
||||
virtual ~SVGFitToViewBox() = default;
|
||||
|
||||
GC::Ref<SVGAnimatedRect> view_box_for_bindings() const { return *m_view_box_for_bindings; }
|
||||
Optional<ViewBox> view_box() const { return m_view_box; }
|
||||
Optional<PreserveAspectRatio> preserve_aspect_ratio() const { return m_preserve_aspect_ratio; }
|
||||
|
||||
protected:
|
||||
void initialize(JS::Realm&);
|
||||
void visit_edges(JS::Cell::Visitor&);
|
||||
void attribute_changed(DOM::Element& element, FlyString const& name, Optional<String> const& value);
|
||||
|
||||
private:
|
||||
Optional<ViewBox> m_view_box;
|
||||
GC::Ptr<SVGAnimatedRect> m_view_box_for_bindings;
|
||||
Optional<PreserveAspectRatio> m_preserve_aspect_ratio;
|
||||
};
|
||||
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
#include <LibWeb/SVG/AttributeParser.h>
|
||||
#include <LibWeb/SVG/SVGAnimatedTransformList.h>
|
||||
#include <LibWeb/SVG/SVGElement.h>
|
||||
#include <LibWeb/SVG/SVGFitToViewBox.h>
|
||||
#include <LibWeb/SVG/SVGGradientElement.h>
|
||||
#include <LibWeb/SVG/TagNames.h>
|
||||
#include <LibWeb/SVG/ViewBox.h>
|
||||
|
@ -47,6 +48,13 @@ public:
|
|||
Optional<FillRule> fill_rule() const;
|
||||
Optional<ClipRule> clip_rule() const;
|
||||
|
||||
virtual Optional<ViewBox> active_view_box() const
|
||||
{
|
||||
if (auto* svg_fit_to_view_box = as_if<SVGFitToViewBox>(*this))
|
||||
return svg_fit_to_view_box->view_box();
|
||||
return {};
|
||||
}
|
||||
|
||||
float visible_stroke_width() const
|
||||
{
|
||||
if (auto color = stroke_color(); color.has_value() && color->alpha() > 0)
|
||||
|
|
|
@ -8,12 +8,10 @@
|
|||
|
||||
#include <LibWeb/SVG/AttributeParser.h>
|
||||
#include <LibWeb/SVG/SVGGraphicsElement.h>
|
||||
#include <LibWeb/SVG/SVGViewport.h>
|
||||
|
||||
namespace Web::SVG {
|
||||
|
||||
class SVGMaskElement final : public SVGGraphicsElement
|
||||
, public SVGViewport {
|
||||
class SVGMaskElement final : public SVGGraphicsElement {
|
||||
|
||||
WEB_PLATFORM_OBJECT(SVGMaskElement, SVGGraphicsElement);
|
||||
GC_DECLARE_ALLOCATOR(SVGMaskElement);
|
||||
|
@ -21,7 +19,7 @@ class SVGMaskElement final : public SVGGraphicsElement
|
|||
public:
|
||||
virtual ~SVGMaskElement() override;
|
||||
|
||||
virtual Optional<ViewBox> view_box() const override
|
||||
virtual Optional<ViewBox> active_view_box() const override
|
||||
{
|
||||
// maskContentUnits = objectBoundingBox acts like the mask is sized to the bounding box
|
||||
// of the target element, with a viewBox of "0 0 1 1".
|
||||
|
@ -30,12 +28,6 @@ public:
|
|||
return {};
|
||||
}
|
||||
|
||||
virtual Optional<PreserveAspectRatio> preserve_aspect_ratio() const override
|
||||
{
|
||||
// preserveAspectRatio = none (allow mask to be scaled in both x and y to match target size)
|
||||
return PreserveAspectRatio { PreserveAspectRatio::Align::None, {} };
|
||||
}
|
||||
|
||||
virtual void attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) override;
|
||||
|
||||
virtual GC::Ptr<Layout::Node> create_layout_node(GC::Ref<CSS::ComputedProperties>) override;
|
||||
|
|
|
@ -34,14 +34,14 @@ void SVGSVGElement::initialize(JS::Realm& realm)
|
|||
{
|
||||
WEB_SET_PROTOTYPE_FOR_INTERFACE(SVGSVGElement);
|
||||
Base::initialize(realm);
|
||||
m_view_box_for_bindings = realm.create<SVGAnimatedRect>(realm);
|
||||
SVGFitToViewBox::initialize(realm);
|
||||
}
|
||||
|
||||
void SVGSVGElement::visit_edges(Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
SVGFitToViewBox::visit_edges(visitor);
|
||||
visitor.visit(m_active_view_element);
|
||||
visitor.visit(m_view_box_for_bindings);
|
||||
}
|
||||
|
||||
GC::Ptr<Layout::Node> SVGSVGElement::create_layout_node(GC::Ref<CSS::ComputedProperties> style)
|
||||
|
@ -120,21 +120,8 @@ void SVGSVGElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties>
|
|||
void SVGSVGElement::attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_)
|
||||
{
|
||||
Base::attribute_changed(name, old_value, value, namespace_);
|
||||
SVGFitToViewBox::attribute_changed(*this, name, value);
|
||||
|
||||
if (name.equals_ignoring_ascii_case(SVG::AttributeNames::viewBox)) {
|
||||
if (!value.has_value()) {
|
||||
m_view_box_for_bindings->set_nulled(true);
|
||||
} else {
|
||||
m_view_box = try_parse_view_box(value.value_or(String {}));
|
||||
m_view_box_for_bindings->set_nulled(!m_view_box.has_value());
|
||||
if (m_view_box.has_value()) {
|
||||
m_view_box_for_bindings->set_base_val(Gfx::DoubleRect { m_view_box->min_x, m_view_box->min_y, m_view_box->width, m_view_box->height });
|
||||
m_view_box_for_bindings->set_anim_val(Gfx::DoubleRect { m_view_box->min_x, m_view_box->min_y, m_view_box->width, m_view_box->height });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (name.equals_ignoring_ascii_case(SVG::AttributeNames::preserveAspectRatio))
|
||||
m_preserve_aspect_ratio = AttributeParser::parse_preserve_aspect_ratio(value.value_or(String {}));
|
||||
if (name.equals_ignoring_ascii_case(SVG::AttributeNames::width) || name.equals_ignoring_ascii_case(SVG::AttributeNames::height))
|
||||
update_fallback_view_box_for_svg_as_image();
|
||||
}
|
||||
|
@ -188,13 +175,13 @@ void SVGSVGElement::set_fallback_view_box_for_svg_as_image(Optional<ViewBox> vie
|
|||
m_fallback_view_box_for_svg_as_image = view_box;
|
||||
}
|
||||
|
||||
Optional<ViewBox> SVGSVGElement::view_box() const
|
||||
Optional<ViewBox> SVGSVGElement::active_view_box() const
|
||||
{
|
||||
if (m_active_view_element && m_active_view_element->view_box().has_value())
|
||||
return m_active_view_element->view_box().value();
|
||||
|
||||
if (m_view_box.has_value())
|
||||
return m_view_box;
|
||||
if (auto view_box = SVGFitToViewBox::view_box(); view_box.has_value())
|
||||
return view_box;
|
||||
|
||||
// NOTE: If the parent is a document, we're an <svg> element used as an image.
|
||||
if (parent() && parent()->is_document() && m_fallback_view_box_for_svg_as_image.has_value())
|
||||
|
|
|
@ -11,17 +11,17 @@
|
|||
#include <LibWeb/Geometry/DOMPoint.h>
|
||||
#include <LibWeb/SVG/AttributeParser.h>
|
||||
#include <LibWeb/SVG/SVGAnimatedLength.h>
|
||||
#include <LibWeb/SVG/SVGFitToViewBox.h>
|
||||
#include <LibWeb/SVG/SVGGraphicsElement.h>
|
||||
#include <LibWeb/SVG/SVGLength.h>
|
||||
#include <LibWeb/SVG/SVGTransform.h>
|
||||
#include <LibWeb/SVG/SVGViewport.h>
|
||||
#include <LibWeb/SVG/ViewBox.h>
|
||||
#include <LibWeb/WebIDL/Types.h>
|
||||
|
||||
namespace Web::SVG {
|
||||
|
||||
class SVGSVGElement final : public SVGGraphicsElement
|
||||
, public SVGViewport {
|
||||
, public SVGFitToViewBox {
|
||||
WEB_PLATFORM_OBJECT(SVGSVGElement, SVGGraphicsElement);
|
||||
GC_DECLARE_ALLOCATOR(SVGSVGElement);
|
||||
|
||||
|
@ -34,16 +34,12 @@ public:
|
|||
virtual bool requires_svg_container() const override { return false; }
|
||||
virtual bool is_svg_container() const override { return true; }
|
||||
|
||||
virtual Optional<ViewBox> view_box() const override;
|
||||
virtual Optional<ViewBox> active_view_box() const override;
|
||||
|
||||
void set_active_view_element(GC::Ptr<SVGViewElement> view_element) { m_active_view_element = view_element; }
|
||||
|
||||
virtual Optional<PreserveAspectRatio> preserve_aspect_ratio() const override { return m_preserve_aspect_ratio; }
|
||||
|
||||
void set_fallback_view_box_for_svg_as_image(Optional<ViewBox>);
|
||||
|
||||
GC::Ref<SVGAnimatedRect> view_box_for_bindings() { return *m_view_box_for_bindings; }
|
||||
|
||||
GC::Ref<SVGAnimatedLength> x() const;
|
||||
GC::Ref<SVGAnimatedLength> y() const;
|
||||
GC::Ref<SVGAnimatedLength> width() const;
|
||||
|
@ -107,13 +103,8 @@ private:
|
|||
|
||||
void update_fallback_view_box_for_svg_as_image();
|
||||
|
||||
Optional<ViewBox> m_view_box;
|
||||
Optional<PreserveAspectRatio> m_preserve_aspect_ratio;
|
||||
|
||||
Optional<ViewBox> m_fallback_view_box_for_svg_as_image;
|
||||
|
||||
GC::Ptr<SVGAnimatedRect> m_view_box_for_bindings;
|
||||
|
||||
GC::Ptr<SVGViewElement> m_active_view_element;
|
||||
};
|
||||
|
||||
|
|
|
@ -30,13 +30,13 @@ void SVGSymbolElement::initialize(JS::Realm& realm)
|
|||
{
|
||||
WEB_SET_PROTOTYPE_FOR_INTERFACE(SVGSymbolElement);
|
||||
Base::initialize(realm);
|
||||
m_view_box_for_bindings = realm.create<SVGAnimatedRect>(realm);
|
||||
SVGFitToViewBox::initialize(realm);
|
||||
}
|
||||
|
||||
void SVGSymbolElement::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_view_box_for_bindings);
|
||||
SVGFitToViewBox::visit_edges(visitor);
|
||||
}
|
||||
|
||||
bool SVGSymbolElement::is_presentational_hint(FlyString const& name) const
|
||||
|
@ -66,15 +66,7 @@ void SVGSymbolElement::apply_presentational_hints(GC::Ref<CSS::CascadedPropertie
|
|||
void SVGSymbolElement::attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_)
|
||||
{
|
||||
Base::attribute_changed(name, old_value, value, namespace_);
|
||||
|
||||
if (name.equals_ignoring_ascii_case(SVG::AttributeNames::viewBox)) {
|
||||
m_view_box = try_parse_view_box(value.value_or(String {}));
|
||||
m_view_box_for_bindings->set_nulled(!m_view_box.has_value());
|
||||
if (m_view_box.has_value()) {
|
||||
m_view_box_for_bindings->set_base_val(Gfx::DoubleRect { m_view_box->min_x, m_view_box->min_y, m_view_box->width, m_view_box->height });
|
||||
m_view_box_for_bindings->set_anim_val(Gfx::DoubleRect { m_view_box->min_x, m_view_box->min_y, m_view_box->width, m_view_box->height });
|
||||
}
|
||||
}
|
||||
SVGFitToViewBox::attribute_changed(*this, name, value);
|
||||
}
|
||||
|
||||
bool SVGSymbolElement::is_direct_child_of_use_shadow_tree() const
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/SVG/SVGFitToViewBox.h>
|
||||
#include <LibWeb/SVG/SVGGraphicsElement.h>
|
||||
#include <LibWeb/SVG/SVGViewport.h>
|
||||
|
||||
namespace Web::SVG {
|
||||
|
||||
class SVGSymbolElement final : public SVGGraphicsElement
|
||||
, public SVGViewport {
|
||||
, public SVGFitToViewBox {
|
||||
WEB_PLATFORM_OBJECT(SVGSymbolElement, SVGGraphicsElement);
|
||||
GC_DECLARE_ALLOCATOR(SVGSymbolElement);
|
||||
|
||||
|
@ -22,15 +22,6 @@ public:
|
|||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
|
||||
virtual Optional<ViewBox> view_box() const override { return m_view_box; }
|
||||
virtual Optional<PreserveAspectRatio> preserve_aspect_ratio() const override
|
||||
{
|
||||
// FIXME: Support the `preserveAspectRatio` attribute on <symbol>.
|
||||
return {};
|
||||
}
|
||||
|
||||
GC::Ref<SVGAnimatedRect> view_box_for_bindings() { return *m_view_box_for_bindings; }
|
||||
|
||||
private:
|
||||
SVGSymbolElement(DOM::Document&, DOM::QualifiedName);
|
||||
|
||||
|
@ -42,10 +33,6 @@ private:
|
|||
bool is_direct_child_of_use_shadow_tree() const;
|
||||
|
||||
virtual void attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) override;
|
||||
|
||||
Optional<ViewBox> m_view_box;
|
||||
|
||||
GC::Ptr<SVGAnimatedRect> m_view_box_for_bindings;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -24,13 +24,13 @@ void SVGViewElement::initialize(JS::Realm& realm)
|
|||
{
|
||||
WEB_SET_PROTOTYPE_FOR_INTERFACE(SVGViewElement);
|
||||
Base::initialize(realm);
|
||||
m_view_box_for_bindings = realm.create<SVGAnimatedRect>(realm);
|
||||
SVGFitToViewBox::initialize(realm);
|
||||
}
|
||||
|
||||
void SVGViewElement::visit_edges(Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_view_box_for_bindings);
|
||||
SVGFitToViewBox::visit_edges(visitor);
|
||||
}
|
||||
|
||||
bool SVGViewElement::is_presentational_hint(FlyString const& name) const
|
||||
|
@ -52,21 +52,7 @@ void SVGViewElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties>
|
|||
void SVGViewElement::attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_)
|
||||
{
|
||||
Base::attribute_changed(name, old_value, value, namespace_);
|
||||
|
||||
if (name.equals_ignoring_ascii_case(SVG::AttributeNames::viewBox)) {
|
||||
if (!value.has_value()) {
|
||||
m_view_box_for_bindings->set_nulled(true);
|
||||
} else {
|
||||
m_view_box = try_parse_view_box(value.value_or(String {}));
|
||||
m_view_box_for_bindings->set_nulled(!m_view_box.has_value());
|
||||
if (m_view_box.has_value()) {
|
||||
m_view_box_for_bindings->set_base_val(Gfx::DoubleRect { m_view_box->min_x, m_view_box->min_y, m_view_box->width, m_view_box->height });
|
||||
m_view_box_for_bindings->set_anim_val(Gfx::DoubleRect { m_view_box->min_x, m_view_box->min_y, m_view_box->width, m_view_box->height });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (name.equals_ignoring_ascii_case(SVG::AttributeNames::preserveAspectRatio))
|
||||
m_preserve_aspect_ratio = AttributeParser::parse_preserve_aspect_ratio(value.value_or(String {}));
|
||||
SVGFitToViewBox::attribute_changed(*this, name, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,14 +6,13 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/SVG/SVGFitToViewBox.h>
|
||||
#include <LibWeb/SVG/SVGGraphicsElement.h>
|
||||
#include <LibWeb/SVG/SVGViewport.h>
|
||||
#include <LibWeb/SVG/ViewBox.h>
|
||||
|
||||
namespace Web::SVG {
|
||||
|
||||
class SVGViewElement final : public SVGGraphicsElement
|
||||
, public SVGViewport {
|
||||
, public SVGFitToViewBox {
|
||||
WEB_PLATFORM_OBJECT(SVGViewElement, SVGGraphicsElement);
|
||||
GC_DECLARE_ALLOCATOR(SVGViewElement);
|
||||
|
||||
|
@ -21,11 +20,6 @@ public:
|
|||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
|
||||
virtual Optional<ViewBox> view_box() const override { return m_view_box; }
|
||||
virtual Optional<PreserveAspectRatio> preserve_aspect_ratio() const override { return m_preserve_aspect_ratio; }
|
||||
|
||||
GC::Ref<SVGAnimatedRect> view_box_for_bindings() { return *m_view_box_for_bindings; }
|
||||
|
||||
private:
|
||||
SVGViewElement(DOM::Document&, DOM::QualifiedName);
|
||||
|
||||
|
@ -35,10 +29,6 @@ private:
|
|||
virtual bool is_svg_view_element() const override { return true; }
|
||||
|
||||
virtual void attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) override;
|
||||
|
||||
Optional<ViewBox> m_view_box;
|
||||
Optional<PreserveAspectRatio> m_preserve_aspect_ratio;
|
||||
GC::Ptr<SVGAnimatedRect> m_view_box_for_bindings;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024, MacDue <macdue@dueutil.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Math.h>
|
||||
#include <LibWeb/SVG/AttributeParser.h>
|
||||
#include <LibWeb/SVG/ViewBox.h>
|
||||
|
||||
namespace Web::SVG {
|
||||
|
||||
class SVGViewport {
|
||||
public:
|
||||
virtual Optional<ViewBox> view_box() const = 0;
|
||||
virtual Optional<PreserveAspectRatio> preserve_aspect_ratio() const = 0;
|
||||
virtual ~SVGViewport() = default;
|
||||
};
|
||||
|
||||
inline CSSPixels normalized_diagonal_length(CSSPixelSize viewport_size)
|
||||
{
|
||||
if (viewport_size.width() == viewport_size.height())
|
||||
return viewport_size.width();
|
||||
return sqrt((viewport_size.width() * viewport_size.width()) + (viewport_size.height() * viewport_size.height())) / CSSPixels::nearest_value_for(AK::Sqrt2<float>);
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue