summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibWeb/SVG/AttributeParser.cpp117
-rw-r--r--Userland/Libraries/LibWeb/SVG/AttributeParser.h47
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;
};