2023-07-22 19:24:55 +01:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
|
2025-11-20 14:57:43 +01:00
|
|
|
* Copyright (c) 2025, Jelle Raaijmakers <jelle@ladybird.org>
|
2023-07-22 19:24:55 +01:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <LibWeb/SVG/SVGTextContentElement.h>
|
|
|
|
|
|
|
|
|
|
namespace Web::SVG {
|
|
|
|
|
|
2025-11-20 14:57:43 +01:00
|
|
|
// https://svgwg.org/svg2-draft/text.html#TSpanNotes
|
|
|
|
|
// https://svgwg.org/svg2-draft/text.html#TSpanAttributes
|
|
|
|
|
struct TextPositioning {
|
|
|
|
|
using Position = Variant<CSS::LengthPercentage, CSS::Number>;
|
|
|
|
|
|
|
|
|
|
Vector<Position> x;
|
|
|
|
|
Vector<Position> y;
|
|
|
|
|
Vector<Position> dx;
|
|
|
|
|
Vector<Position> dy;
|
|
|
|
|
Vector<float> rotate;
|
|
|
|
|
|
|
|
|
|
void apply_to_text_position(Layout::Node const& node, CSSPixelSize viewport, Gfx::FloatPoint& current_text_position,
|
|
|
|
|
size_t character_index) const
|
|
|
|
|
{
|
|
|
|
|
auto value_for_character = [&](Vector<Position> const& values) -> float {
|
|
|
|
|
if (values.is_empty())
|
|
|
|
|
return 0.f;
|
|
|
|
|
|
|
|
|
|
auto position = character_index < values.size() ? values[character_index] : values.last();
|
|
|
|
|
return position.visit(
|
|
|
|
|
[](CSS::Number const& number) { return static_cast<float>(number.value()); },
|
|
|
|
|
[&](CSS::LengthPercentage const& length_percentage) {
|
|
|
|
|
auto reference = &values == &x || &values == &dx ? viewport.width() : viewport.height();
|
|
|
|
|
return length_percentage.to_px(node, reference).to_float();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!x.is_empty())
|
|
|
|
|
current_text_position.set_x(value_for_character(x));
|
|
|
|
|
if (!y.is_empty())
|
|
|
|
|
current_text_position.set_y(value_for_character(y));
|
|
|
|
|
|
|
|
|
|
current_text_position.translate_by(value_for_character(dx), value_for_character(dy));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-07-22 19:24:55 +01:00
|
|
|
// https://svgwg.org/svg2-draft/text.html#InterfaceSVGTextPositioningElement
|
|
|
|
|
class SVGTextPositioningElement : public SVGTextContentElement {
|
|
|
|
|
WEB_PLATFORM_OBJECT(SVGTextPositioningElement, SVGTextContentElement);
|
|
|
|
|
|
|
|
|
|
public:
|
2024-11-14 08:14:16 -05:00
|
|
|
virtual void attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) override;
|
2023-07-22 19:24:55 +01:00
|
|
|
|
2025-11-20 14:57:43 +01:00
|
|
|
TextPositioning text_positioning() const;
|
2023-07-22 19:24:55 +01:00
|
|
|
|
2025-11-20 14:57:43 +01:00
|
|
|
GC::Ref<SVGAnimatedLengthList> x();
|
|
|
|
|
GC::Ref<SVGAnimatedLengthList> y();
|
|
|
|
|
GC::Ref<SVGAnimatedLengthList> dx();
|
|
|
|
|
GC::Ref<SVGAnimatedLengthList> dy();
|
|
|
|
|
GC::Ref<SVGAnimatedNumberList> rotate();
|
2023-07-22 19:24:55 +01:00
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
SVGTextPositioningElement(DOM::Document&, DOM::QualifiedName);
|
|
|
|
|
|
2023-08-07 08:41:28 +02:00
|
|
|
virtual void initialize(JS::Realm&) override;
|
2025-11-20 14:57:43 +01:00
|
|
|
virtual void visit_edges(Visitor&) override;
|
2023-07-22 19:24:55 +01:00
|
|
|
|
|
|
|
|
private:
|
2025-11-20 14:57:43 +01:00
|
|
|
GC::Ref<SVGAnimatedLengthList> ensure_length_list(GC::Ptr<SVGAnimatedLengthList>&, FlyString const& attribute_name) const;
|
|
|
|
|
|
|
|
|
|
GC::Ptr<SVGAnimatedLengthList> m_x;
|
|
|
|
|
GC::Ptr<SVGAnimatedLengthList> m_y;
|
|
|
|
|
GC::Ptr<SVGAnimatedLengthList> m_dx;
|
|
|
|
|
GC::Ptr<SVGAnimatedLengthList> m_dy;
|
|
|
|
|
GC::Ptr<SVGAnimatedNumberList> m_rotate;
|
2023-07-22 19:24:55 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|