LibGfx+LibWeb: Implement SVGTextPathElement.startOffset
This commit is contained in:
parent
d92f93f34e
commit
ad7105b570
10 changed files with 83 additions and 19 deletions
|
|
@ -47,13 +47,14 @@ public:
|
|||
[[nodiscard]] virtual bool is_empty() const = 0;
|
||||
virtual Gfx::FloatPoint last_point() const = 0;
|
||||
virtual Gfx::FloatRect bounding_box() const = 0;
|
||||
virtual float length() const = 0;
|
||||
virtual void set_fill_type(Gfx::WindingRule winding_rule) = 0;
|
||||
virtual bool contains(FloatPoint point, Gfx::WindingRule) const = 0;
|
||||
|
||||
virtual NonnullOwnPtr<PathImpl> clone() const = 0;
|
||||
virtual NonnullOwnPtr<PathImpl> copy_transformed(Gfx::AffineTransform const&) const = 0;
|
||||
virtual NonnullOwnPtr<PathImpl> place_text_along(Utf8View const& text, Font const&) const = 0;
|
||||
virtual NonnullOwnPtr<PathImpl> place_text_along(Utf16View const& text, Font const&) const = 0;
|
||||
virtual NonnullOwnPtr<PathImpl> place_text_along(Utf8View const& text, Font const&, float offset = 0) const = 0;
|
||||
virtual NonnullOwnPtr<PathImpl> place_text_along(Utf16View const& text, Font const&, float offset = 0) const = 0;
|
||||
|
||||
virtual String to_svg_string() const = 0;
|
||||
};
|
||||
|
|
@ -116,13 +117,14 @@ public:
|
|||
[[nodiscard]] bool is_empty() const { return impl().is_empty(); }
|
||||
Gfx::FloatPoint last_point() const { return impl().last_point(); }
|
||||
Gfx::FloatRect bounding_box() const { return impl().bounding_box(); }
|
||||
float length() const { return impl().length(); }
|
||||
bool contains(FloatPoint point, Gfx::WindingRule winding_rule) const { return impl().contains(point, winding_rule); }
|
||||
void set_fill_type(Gfx::WindingRule winding_rule) { impl().set_fill_type(winding_rule); }
|
||||
|
||||
Gfx::Path clone() const { return Gfx::Path { impl().clone() }; }
|
||||
Gfx::Path copy_transformed(Gfx::AffineTransform const& transform) const { return Gfx::Path { impl().copy_transformed(transform) }; }
|
||||
Gfx::Path place_text_along(Utf8View const& text, Font const& font) const { return Gfx::Path { impl().place_text_along(text, font) }; }
|
||||
Gfx::Path place_text_along(Utf16View const& text, Font const& font) const { return Gfx::Path { impl().place_text_along(text, font) }; }
|
||||
Gfx::Path place_text_along(Utf8View const& text, Font const& font, float offset = 0) const { return Gfx::Path { impl().place_text_along(text, font, offset) }; }
|
||||
Gfx::Path place_text_along(Utf16View const& text, Font const& font, float offset = 0) const { return Gfx::Path { impl().place_text_along(text, font, offset) }; }
|
||||
|
||||
String to_svg_string() const { return impl().to_svg_string(); }
|
||||
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ void PathImplSkia::offset(Gfx::FloatPoint const& offset)
|
|||
}
|
||||
|
||||
template<typename TextToGlyphs>
|
||||
static NonnullOwnPtr<PathImpl> place_text_along_impl(SkPath const& path, Font const& font, size_t length_in_code_points, TextToGlyphs&& text_to_glyphs)
|
||||
static NonnullOwnPtr<PathImpl> place_text_along_impl(SkPath const& path, Font const& font, size_t length_in_code_points, float offset, TextToGlyphs&& text_to_glyphs)
|
||||
{
|
||||
auto sk_font = font.skia_font(1);
|
||||
SkScalar x = 0;
|
||||
|
|
@ -142,7 +142,7 @@ static NonnullOwnPtr<PathImpl> place_text_along_impl(SkPath const& path, Font co
|
|||
text_to_glyphs(sk_font, run_buffer);
|
||||
|
||||
SkPathMeasure path_measure(path, false);
|
||||
SkScalar accumulated_distance = 0;
|
||||
SkScalar accumulated_distance = offset;
|
||||
|
||||
auto output_path = PathImplSkia::create();
|
||||
SkScalar path_length = path_measure.getLength();
|
||||
|
|
@ -178,23 +178,23 @@ static NonnullOwnPtr<PathImpl> place_text_along_impl(SkPath const& path, Font co
|
|||
return output_path;
|
||||
}
|
||||
|
||||
NonnullOwnPtr<PathImpl> PathImplSkia::place_text_along(Utf8View const& text, Font const& font) const
|
||||
NonnullOwnPtr<PathImpl> PathImplSkia::place_text_along(Utf8View const& text, Font const& font, float offset) const
|
||||
{
|
||||
auto length_in_code_points = text.length();
|
||||
|
||||
return place_text_along_impl(*m_path, font, length_in_code_points, [&](auto const& sk_font, auto const& run_buffer) {
|
||||
return place_text_along_impl(*m_path, font, length_in_code_points, offset, [&](auto const& sk_font, auto const& run_buffer) {
|
||||
sk_font.textToGlyphs(text.as_string().characters_without_null_termination(), text.as_string().length(), SkTextEncoding::kUTF8, run_buffer.glyphs, length_in_code_points);
|
||||
});
|
||||
}
|
||||
|
||||
NonnullOwnPtr<PathImpl> PathImplSkia::place_text_along(Utf16View const& text, Font const& font) const
|
||||
NonnullOwnPtr<PathImpl> PathImplSkia::place_text_along(Utf16View const& text, Font const& font, float offset) const
|
||||
{
|
||||
if (text.has_ascii_storage())
|
||||
return place_text_along(Utf8View { text.bytes() }, font);
|
||||
return place_text_along(Utf8View { text.bytes() }, font, offset);
|
||||
|
||||
auto length_in_code_points = text.length_in_code_points();
|
||||
|
||||
return place_text_along_impl(*m_path, font, length_in_code_points, [&](auto const& sk_font, auto const& run_buffer) {
|
||||
return place_text_along_impl(*m_path, font, length_in_code_points, offset, [&](auto const& sk_font, auto const& run_buffer) {
|
||||
sk_font.textToGlyphs(text.utf16_span().data(), text.length_in_code_units() * sizeof(char16_t), SkTextEncoding::kUTF16, run_buffer.glyphs, length_in_code_points);
|
||||
});
|
||||
}
|
||||
|
|
@ -243,6 +243,12 @@ Gfx::FloatRect PathImplSkia::bounding_box() const
|
|||
return { bounds.fLeft, bounds.fTop, bounds.fRight - bounds.fLeft, bounds.fBottom - bounds.fTop };
|
||||
}
|
||||
|
||||
float PathImplSkia::length() const
|
||||
{
|
||||
SkPathMeasure path_measure(*m_path, false);
|
||||
return path_measure.getLength();
|
||||
}
|
||||
|
||||
bool PathImplSkia::contains(FloatPoint point, Gfx::WindingRule winding_rule) const
|
||||
{
|
||||
SkPath temp_path = *m_path;
|
||||
|
|
|
|||
|
|
@ -40,13 +40,14 @@ public:
|
|||
[[nodiscard]] virtual bool is_empty() const override;
|
||||
virtual Gfx::FloatPoint last_point() const override;
|
||||
virtual Gfx::FloatRect bounding_box() const override;
|
||||
virtual float length() const override;
|
||||
virtual bool contains(FloatPoint point, Gfx::WindingRule) const override;
|
||||
virtual void set_fill_type(Gfx::WindingRule winding_rule) override;
|
||||
|
||||
virtual NonnullOwnPtr<PathImpl> clone() const override;
|
||||
virtual NonnullOwnPtr<PathImpl> copy_transformed(Gfx::AffineTransform const&) const override;
|
||||
virtual NonnullOwnPtr<PathImpl> place_text_along(Utf8View const& text, Font const&) const override;
|
||||
virtual NonnullOwnPtr<PathImpl> place_text_along(Utf16View const& text, Font const&) const override;
|
||||
virtual NonnullOwnPtr<PathImpl> place_text_along(Utf8View const& text, Font const&, float offset = 0) const override;
|
||||
virtual NonnullOwnPtr<PathImpl> place_text_along(Utf16View const& text, Font const&, float offset = 0) const override;
|
||||
|
||||
virtual String to_svg_string() const override;
|
||||
|
||||
|
|
|
|||
|
|
@ -460,7 +460,8 @@ Gfx::Path SVGFormattingContext::compute_path_for_text_path(SVGTextPathBox const&
|
|||
auto text_contents = rendered_text_contents(text_path_element);
|
||||
|
||||
auto shape_path = const_cast<SVG::SVGGeometryElement&>(*path_or_shape).get_path(m_viewport_size);
|
||||
return shape_path.place_text_along(text_contents, font);
|
||||
auto start_offset = text_path_element.start_offset_for_path_length(shape_path.length());
|
||||
return shape_path.place_text_along(text_contents, font, start_offset);
|
||||
}
|
||||
|
||||
void SVGFormattingContext::layout_path_like_element(SVGGraphicsBox const& graphics_box)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include <LibWeb/Bindings/SVGTextPathElement.h>
|
||||
#include <LibWeb/Layout/SVGTextPathBox.h>
|
||||
#include <LibWeb/SVG/AttributeNames.h>
|
||||
#include <LibWeb/SVG/SVGLength.h>
|
||||
#include <LibWeb/SVG/SVGTextPathElement.h>
|
||||
|
||||
namespace Web::SVG {
|
||||
|
|
@ -19,6 +20,14 @@ SVGTextPathElement::SVGTextPathElement(DOM::Document& document, DOM::QualifiedNa
|
|||
{
|
||||
}
|
||||
|
||||
void SVGTextPathElement::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 == SVG::AttributeNames::startOffset)
|
||||
m_start_offset = AttributeParser::parse_number_percentage(value.value_or(String {}));
|
||||
}
|
||||
|
||||
GC::Ptr<SVGGeometryElement const> SVGTextPathElement::path_or_shape() const
|
||||
{
|
||||
auto href = has_attribute(AttributeNames::href) ? get_attribute(AttributeNames::href) : get_attribute(AttributeNames::xlink_href);
|
||||
|
|
@ -27,6 +36,22 @@ GC::Ptr<SVGGeometryElement const> SVGTextPathElement::path_or_shape() const
|
|||
return try_resolve_url_to<SVGGeometryElement const>(*href);
|
||||
}
|
||||
|
||||
// https://svgwg.org/svg2-draft/text.html#TextPathElementStartOffsetAttribute
|
||||
float SVGTextPathElement::start_offset_for_path_length(float path_length) const
|
||||
{
|
||||
if (!m_start_offset.has_value())
|
||||
return 0;
|
||||
return m_start_offset->resolve_relative_to(path_length);
|
||||
}
|
||||
|
||||
// https://svgwg.org/svg2-draft/text.html#__svg__SVGTextPathElement__startOffset
|
||||
GC::Ref<SVGAnimatedLength> SVGTextPathElement::start_offset() const
|
||||
{
|
||||
auto base_length = SVGLength::create(realm(), 0, m_start_offset.value_or(NumberPercentage::create_number(0)).value(), SVGLength::ReadOnly::No);
|
||||
auto anim_length = SVGLength::create(realm(), 0, m_start_offset.value_or(NumberPercentage::create_number(0)).value(), SVGLength::ReadOnly::Yes);
|
||||
return SVGAnimatedLength::create(realm(), base_length, anim_length);
|
||||
}
|
||||
|
||||
void SVGTextPathElement::initialize(JS::Realm& realm)
|
||||
{
|
||||
WEB_SET_PROTOTYPE_FOR_INTERFACE(SVGTextPathElement);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/SVG/AttributeParser.h>
|
||||
#include <LibWeb/SVG/SVGAnimatedLength.h>
|
||||
#include <LibWeb/SVG/SVGGeometryElement.h>
|
||||
#include <LibWeb/SVG/SVGTextContentElement.h>
|
||||
#include <LibWeb/SVG/SVGURIReference.h>
|
||||
|
|
@ -24,11 +26,19 @@ public:
|
|||
|
||||
GC::Ptr<SVGGeometryElement const> path_or_shape() const;
|
||||
|
||||
float start_offset_for_path_length(float path_length) const;
|
||||
|
||||
GC::Ref<SVGAnimatedLength> start_offset() const;
|
||||
|
||||
protected:
|
||||
SVGTextPathElement(DOM::Document&, DOM::QualifiedName);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
virtual void attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) override;
|
||||
|
||||
private:
|
||||
Optional<NumberPercentage> m_start_offset;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ interface SVGTextPathElement : SVGTextContentElement {
|
|||
const unsigned short TEXTPATH_SPACINGTYPE_AUTO = 1;
|
||||
const unsigned short TEXTPATH_SPACINGTYPE_EXACT = 2;
|
||||
|
||||
[FIXME, SameObject] readonly attribute SVGAnimatedLength startOffset;
|
||||
[SameObject] readonly attribute SVGAnimatedLength startOffset;
|
||||
[FIXME, SameObject] readonly attribute SVGAnimatedEnumeration method;
|
||||
[FIXME, SameObject] readonly attribute SVGAnimatedEnumeration spacing;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<svg width="300" height="100" viewBox="0 0 300 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<path id="line" d="M 150 50 h 100"/>
|
||||
</defs>
|
||||
<text font-family="sans-serif" font-size="20">
|
||||
<textPath href="#line">Test</textPath>
|
||||
</text>
|
||||
</svg>
|
||||
10
Tests/LibWeb/Ref/input/svg-textPath-startOffset.html
Normal file
10
Tests/LibWeb/Ref/input/svg-textPath-startOffset.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<link rel="match" href="../expected/svg-textPath-startOffset-ref.html">
|
||||
<svg width="300" height="100" viewBox="0 0 300 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<path id="line" d="M 50 50 h 200"/>
|
||||
</defs>
|
||||
<text font-family="sans-serif" font-size="20">
|
||||
<textPath href="#line" startOffset="50%">Test</textPath>
|
||||
</text>
|
||||
</svg>
|
||||
|
|
@ -2,8 +2,8 @@ Harness status: OK
|
|||
|
||||
Found 1780 tests
|
||||
|
||||
1101 Pass
|
||||
679 Fail
|
||||
1103 Pass
|
||||
677 Fail
|
||||
Pass idl_test setup
|
||||
Pass idl_test validation
|
||||
Pass Partial interface Document: original interface defined
|
||||
|
|
@ -1236,7 +1236,7 @@ Pass SVGTextPathElement interface: constant TEXTPATH_SPACINGTYPE_AUTO on interfa
|
|||
Pass SVGTextPathElement interface: constant TEXTPATH_SPACINGTYPE_AUTO on interface prototype object
|
||||
Pass SVGTextPathElement interface: constant TEXTPATH_SPACINGTYPE_EXACT on interface object
|
||||
Pass SVGTextPathElement interface: constant TEXTPATH_SPACINGTYPE_EXACT on interface prototype object
|
||||
Fail SVGTextPathElement interface: attribute startOffset
|
||||
Pass SVGTextPathElement interface: attribute startOffset
|
||||
Fail SVGTextPathElement interface: attribute method
|
||||
Fail SVGTextPathElement interface: attribute spacing
|
||||
Pass SVGTextPathElement interface: attribute href
|
||||
|
|
@ -1248,7 +1248,7 @@ Pass SVGTextPathElement interface: objects.textPath must inherit property "TEXTP
|
|||
Pass SVGTextPathElement interface: objects.textPath must inherit property "TEXTPATH_SPACINGTYPE_UNKNOWN" with the proper type
|
||||
Pass SVGTextPathElement interface: objects.textPath must inherit property "TEXTPATH_SPACINGTYPE_AUTO" with the proper type
|
||||
Pass SVGTextPathElement interface: objects.textPath must inherit property "TEXTPATH_SPACINGTYPE_EXACT" with the proper type
|
||||
Fail SVGTextPathElement interface: objects.textPath must inherit property "startOffset" with the proper type
|
||||
Pass SVGTextPathElement interface: objects.textPath must inherit property "startOffset" with the proper type
|
||||
Fail SVGTextPathElement interface: objects.textPath must inherit property "method" with the proper type
|
||||
Fail SVGTextPathElement interface: objects.textPath must inherit property "spacing" with the proper type
|
||||
Pass SVGTextPathElement interface: objects.textPath must inherit property "href" with the proper type
|
||||
|
|
|
|||
Loading…
Reference in a new issue