diff options
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Libraries/LibWeb/SVG/AttributeParser.cpp | 117 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/SVG/AttributeParser.h | 47 |
2 files changed, 156 insertions, 8 deletions
diff --git a/Userland/Libraries/LibWeb/SVG/AttributeParser.cpp b/Userland/Libraries/LibWeb/SVG/AttributeParser.cpp index 236f707b7a..05ca7537f4 100644 --- a/Userland/Libraries/LibWeb/SVG/AttributeParser.cpp +++ b/Userland/Libraries/LibWeb/SVG/AttributeParser.cpp @@ -1,22 +1,30 @@ /* * Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org> * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org> + * Copyright (c) 2023, MacDue <macdue@dueutil.tech> * * SPDX-License-Identifier: BSD-2-Clause */ #include "AttributeParser.h" #include <AK/FloatingPointStringConversions.h> +#include <AK/GenericShorthands.h> #include <AK/StringBuilder.h> #include <ctype.h> namespace Web::SVG { AttributeParser::AttributeParser(StringView source) - : m_source(move(source)) + : m_lexer(source) { } +Optional<Vector<Transform>> AttributeParser::parse_transform(StringView input) +{ + AttributeParser parser { input }; + return parser.parse_transform(); +} + Vector<PathInstruction> AttributeParser::parse_path_data(StringView input) { AttributeParser parser { input }; @@ -361,12 +369,12 @@ float AttributeParser::parse_nonnegative_number() // at the start. That condition should have been checked by the caller. VERIFY(!match('+') && !match('-')); - auto remaining_source_text = m_source.substring_view(m_cursor); + auto remaining_source_text = m_lexer.remaining(); char const* start = remaining_source_text.characters_without_null_termination(); auto maybe_float = parse_first_floating_point<float>(start, start + remaining_source_text.length()); VERIFY(maybe_float.parsed_value()); - m_cursor += maybe_float.end_ptr - start; + m_lexer.ignore(maybe_float.end_ptr - start); return maybe_float.value; } @@ -389,6 +397,109 @@ int AttributeParser::parse_sign() return 1; } +// https://drafts.csswg.org/css-transforms/#svg-syntax +Optional<Vector<Transform>> AttributeParser::parse_transform() +{ + // wsp: + // Either a U+000A LINE FEED, U+000D CARRIAGE RETURN, U+0009 CHARACTER TABULATION, or U+0020 SPACE. + auto wsp = [](char c) { + return AK::first_is_one_of(c, '\n', '\r', '\t', '\f', ' '); + }; + auto consume_whitespace = [&] { + m_lexer.consume_while(wsp); + }; + + auto consume_comma_whitespace = [&] { + consume_whitespace(); + m_lexer.consume_specific(','); + consume_whitespace(); + }; + + // FIXME: AttributeParser currently does not handle invalid parses in most cases (e.g. parse_number()) and just crashes. + auto parse_optional_number = [&](float default_value = 0.0f) { + consume_comma_whitespace(); + if (m_lexer.next_is(isdigit)) + return parse_number(); + return default_value; + }; + + auto parse_function = [&](auto body) -> Optional<Transform> { + consume_whitespace(); + if (!m_lexer.consume_specific('(')) + return {}; + consume_whitespace(); + Transform transform { .operation = Transform::Operation { body() } }; + consume_whitespace(); + if (m_lexer.consume_specific(')')) + return transform; + return {}; + }; + + // NOTE: This looks very similar to the CSS transform but the syntax is not compatible. + Vector<Transform> transform_list; + consume_whitespace(); + while (!done()) { + Optional<Transform> maybe_transform; + if (m_lexer.consume_specific("translate"sv)) { + maybe_transform = parse_function([&] { + Transform::Translate translate {}; + translate.x = parse_number(); + translate.y = parse_optional_number(); + return translate; + }); + } else if (m_lexer.consume_specific("scale"sv)) { + maybe_transform = parse_function([&] { + Transform::Scale scale {}; + scale.x = parse_number(); + scale.y = parse_optional_number(scale.x); + return scale; + }); + } else if (m_lexer.consume_specific("rotate"sv)) { + maybe_transform = parse_function([&] { + Transform::Rotate rotate {}; + rotate.a = parse_number(); + rotate.x = parse_optional_number(); + rotate.y = parse_optional_number(); + return rotate; + }); + } else if (m_lexer.consume_specific("skewX"sv)) { + maybe_transform = parse_function([&] { + Transform::SkewX skew_x {}; + skew_x.a = parse_number(); + return skew_x; + }); + } else if (m_lexer.consume_specific("skewY"sv)) { + maybe_transform = parse_function([&] { + Transform::SkewY skew_y {}; + skew_y.a = parse_number(); + return skew_y; + }); + } else if (m_lexer.consume_specific("matrix"sv)) { + maybe_transform = parse_function([&] { + Transform::Matrix matrix; + matrix.a = parse_number(); + consume_comma_whitespace(); + matrix.b = parse_number(); + consume_comma_whitespace(); + matrix.c = parse_number(); + consume_comma_whitespace(); + matrix.d = parse_number(); + consume_comma_whitespace(); + matrix.e = parse_number(); + consume_comma_whitespace(); + matrix.f = parse_number(); + return matrix; + }); + } + if (maybe_transform.has_value()) + transform_list.append(*maybe_transform); + else + return {}; + consume_comma_whitespace(); + } + return transform_list; +} + bool AttributeParser::match_whitespace() const { if (done()) diff --git a/Userland/Libraries/LibWeb/SVG/AttributeParser.h b/Userland/Libraries/LibWeb/SVG/AttributeParser.h index 618406554a..e7b5e075c5 100644 --- a/Userland/Libraries/LibWeb/SVG/AttributeParser.h +++ b/Userland/Libraries/LibWeb/SVG/AttributeParser.h @@ -7,6 +7,8 @@ #pragma once +#include <AK/GenericLexer.h> +#include <AK/Variant.h> #include <AK/Vector.h> #include <LibGfx/Point.h> @@ -32,6 +34,39 @@ struct PathInstruction { Vector<float> data; }; +struct Transform { + struct Translate { + float x; + float y; + }; + struct Scale { + float x; + float y; + }; + struct Rotate { + float a; + float x; + float y; + }; + struct SkewX { + float a; + }; + struct SkewY { + float a; + }; + struct Matrix { + float a; + float b; + float c; + float d; + float e; + float f; + }; + + using Operation = Variant<Translate, Scale, Rotate, SkewX, SkewY, Matrix>; + Operation operation; +}; + class AttributeParser final { public: ~AttributeParser() = default; @@ -41,6 +76,7 @@ public: static Optional<float> parse_positive_length(StringView input); static Vector<Gfx::FloatPoint> parse_points(StringView input); static Vector<PathInstruction> parse_path_data(StringView input); + static Optional<Vector<Transform>> parse_transform(StringView input); private: AttributeParser(StringView source); @@ -57,6 +93,8 @@ private: void parse_smooth_quadratic_bezier_curveto(); void parse_elliptical_arc(); + Optional<Vector<Transform>> parse_transform(); + float parse_length(); float parse_coordinate(); Vector<float> parse_coordinate_pair(); @@ -79,12 +117,11 @@ private: bool match_length() const; bool match(char c) const { return !done() && ch() == c; } - bool done() const { return m_cursor >= m_source.length(); } - char ch() const { return m_source[m_cursor]; } - char consume() { return m_source[m_cursor++]; } + bool done() const { return m_lexer.is_eof(); } + char ch() const { return m_lexer.peek(); } + char consume() { return m_lexer.consume(); } - StringView m_source; - size_t m_cursor { 0 }; + GenericLexer m_lexer; Vector<PathInstruction> m_instructions; }; |