소스 검색

LibWeb: Move SVG::PathDataParser into its own file and rename it

I've chosen the name `AttributeParser` since it parses data from
attributes. Rather than duplicate the parsing of numbers and other
basic types, let's make use of this existing parsing code for parsing
the data for `<line>`, `<polyline>`, etc.
Sam Atkins 3 년 전
부모
커밋
82308fb71a

+ 1 - 0
Userland/Libraries/LibWeb/CMakeLists.txt

@@ -265,6 +265,7 @@ set(SOURCES
     RequestIdleCallback/IdleDeadline.cpp
     RequestIdleCallback/IdleDeadline.cpp
     ResizeObserver/ResizeObserver.cpp
     ResizeObserver/ResizeObserver.cpp
     SVG/AttributeNames.cpp
     SVG/AttributeNames.cpp
+    SVG/AttributeParser.cpp
     SVG/SVGElement.cpp
     SVG/SVGElement.cpp
     SVG/SVGGElement.cpp
     SVG/SVGGElement.cpp
     SVG/SVGGeometryElement.cpp
     SVG/SVGGeometryElement.cpp

+ 367 - 0
Userland/Libraries/LibWeb/SVG/AttributeParser.cpp

@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org>
+ * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "AttributeParser.h"
+#include <AK/StringBuilder.h>
+#include <ctype.h>
+
+namespace Web::SVG {
+
+AttributeParser::AttributeParser(String source)
+    : m_source(move(source))
+{
+}
+
+Vector<PathInstruction> AttributeParser::parse_path_data()
+{
+    parse_whitespace();
+    while (!done())
+        parse_drawto();
+    if (!m_instructions.is_empty() && m_instructions[0].type != PathInstructionType::Move)
+        VERIFY_NOT_REACHED();
+    return m_instructions;
+}
+
+void AttributeParser::parse_drawto()
+{
+    if (match('M') || match('m')) {
+        parse_moveto();
+    } else if (match('Z') || match('z')) {
+        parse_closepath();
+    } else if (match('L') || match('l')) {
+        parse_lineto();
+    } else if (match('H') || match('h')) {
+        parse_horizontal_lineto();
+    } else if (match('V') || match('v')) {
+        parse_vertical_lineto();
+    } else if (match('C') || match('c')) {
+        parse_curveto();
+    } else if (match('S') || match('s')) {
+        parse_smooth_curveto();
+    } else if (match('Q') || match('q')) {
+        parse_quadratic_bezier_curveto();
+    } else if (match('T') || match('t')) {
+        parse_smooth_quadratic_bezier_curveto();
+    } else if (match('A') || match('a')) {
+        parse_elliptical_arc();
+    } else {
+        dbgln("AttributeParser::parse_drawto failed to match: '{}'", ch());
+        TODO();
+    }
+}
+
+void AttributeParser::parse_moveto()
+{
+    bool absolute = consume() == 'M';
+    parse_whitespace();
+    for (auto& pair : parse_coordinate_pair_sequence())
+        m_instructions.append({ PathInstructionType::Move, absolute, pair });
+}
+
+void AttributeParser::parse_closepath()
+{
+    bool absolute = consume() == 'Z';
+    parse_whitespace();
+    m_instructions.append({ PathInstructionType::ClosePath, absolute, {} });
+}
+
+void AttributeParser::parse_lineto()
+{
+    bool absolute = consume() == 'L';
+    parse_whitespace();
+    for (auto& pair : parse_coordinate_pair_sequence())
+        m_instructions.append({ PathInstructionType::Line, absolute, pair });
+}
+
+void AttributeParser::parse_horizontal_lineto()
+{
+    bool absolute = consume() == 'H';
+    parse_whitespace();
+    m_instructions.append({ PathInstructionType::HorizontalLine, absolute, parse_coordinate_sequence() });
+}
+
+void AttributeParser::parse_vertical_lineto()
+{
+    bool absolute = consume() == 'V';
+    parse_whitespace();
+    m_instructions.append({ PathInstructionType::VerticalLine, absolute, parse_coordinate_sequence() });
+}
+
+void AttributeParser::parse_curveto()
+{
+    bool absolute = consume() == 'C';
+    parse_whitespace();
+
+    while (true) {
+        m_instructions.append({ PathInstructionType::Curve, absolute, parse_coordinate_pair_triplet() });
+        if (match_comma_whitespace())
+            parse_comma_whitespace();
+        if (!match_coordinate())
+            break;
+    }
+}
+
+void AttributeParser::parse_smooth_curveto()
+{
+    bool absolute = consume() == 'S';
+    parse_whitespace();
+
+    while (true) {
+        m_instructions.append({ PathInstructionType::SmoothCurve, absolute, parse_coordinate_pair_double() });
+        if (match_comma_whitespace())
+            parse_comma_whitespace();
+        if (!match_coordinate())
+            break;
+    }
+}
+
+void AttributeParser::parse_quadratic_bezier_curveto()
+{
+    bool absolute = consume() == 'Q';
+    parse_whitespace();
+
+    while (true) {
+        m_instructions.append({ PathInstructionType::QuadraticBezierCurve, absolute, parse_coordinate_pair_double() });
+        if (match_comma_whitespace())
+            parse_comma_whitespace();
+        if (!match_coordinate())
+            break;
+    }
+}
+
+void AttributeParser::parse_smooth_quadratic_bezier_curveto()
+{
+    bool absolute = consume() == 'T';
+    parse_whitespace();
+
+    while (true) {
+        m_instructions.append({ PathInstructionType::SmoothQuadraticBezierCurve, absolute, parse_coordinate_pair() });
+        if (match_comma_whitespace())
+            parse_comma_whitespace();
+        if (!match_coordinate())
+            break;
+    }
+}
+
+void AttributeParser::parse_elliptical_arc()
+{
+    bool absolute = consume() == 'A';
+    parse_whitespace();
+
+    while (true) {
+        m_instructions.append({ PathInstructionType::EllipticalArc, absolute, parse_elliptical_arg_argument() });
+        if (match_comma_whitespace())
+            parse_comma_whitespace();
+        if (!match_coordinate())
+            break;
+    }
+}
+
+float AttributeParser::parse_coordinate()
+{
+    return parse_sign() * parse_number();
+}
+
+Vector<float> AttributeParser::parse_coordinate_pair()
+{
+    Vector<float> coordinates;
+    coordinates.append(parse_coordinate());
+    if (match_comma_whitespace())
+        parse_comma_whitespace();
+    coordinates.append(parse_coordinate());
+    return coordinates;
+}
+
+Vector<float> AttributeParser::parse_coordinate_sequence()
+{
+    Vector<float> sequence;
+    while (true) {
+        sequence.append(parse_coordinate());
+        if (match_comma_whitespace())
+            parse_comma_whitespace();
+        if (!match_comma_whitespace() && !match_coordinate())
+            break;
+    }
+    return sequence;
+}
+
+Vector<Vector<float>> AttributeParser::parse_coordinate_pair_sequence()
+{
+    Vector<Vector<float>> sequence;
+    while (true) {
+        sequence.append(parse_coordinate_pair());
+        if (match_comma_whitespace())
+            parse_comma_whitespace();
+        if (!match_comma_whitespace() && !match_coordinate())
+            break;
+    }
+    return sequence;
+}
+
+Vector<float> AttributeParser::parse_coordinate_pair_double()
+{
+    Vector<float> coordinates;
+    coordinates.extend(parse_coordinate_pair());
+    if (match_comma_whitespace())
+        parse_comma_whitespace();
+    coordinates.extend(parse_coordinate_pair());
+    return coordinates;
+}
+
+Vector<float> AttributeParser::parse_coordinate_pair_triplet()
+{
+    Vector<float> coordinates;
+    coordinates.extend(parse_coordinate_pair());
+    if (match_comma_whitespace())
+        parse_comma_whitespace();
+    coordinates.extend(parse_coordinate_pair());
+    if (match_comma_whitespace())
+        parse_comma_whitespace();
+    coordinates.extend(parse_coordinate_pair());
+    return coordinates;
+}
+
+Vector<float> AttributeParser::parse_elliptical_arg_argument()
+{
+    Vector<float> numbers;
+    numbers.append(parse_number());
+    if (match_comma_whitespace())
+        parse_comma_whitespace();
+    numbers.append(parse_number());
+    if (match_comma_whitespace())
+        parse_comma_whitespace();
+    numbers.append(parse_number());
+    parse_comma_whitespace();
+    numbers.append(parse_flag());
+    if (match_comma_whitespace())
+        parse_comma_whitespace();
+    numbers.append(parse_flag());
+    if (match_comma_whitespace())
+        parse_comma_whitespace();
+    numbers.extend(parse_coordinate_pair());
+
+    return numbers;
+}
+
+void AttributeParser::parse_whitespace(bool must_match_once)
+{
+    bool matched = false;
+    while (!done() && match_whitespace()) {
+        consume();
+        matched = true;
+    }
+
+    VERIFY(!must_match_once || matched);
+}
+
+void AttributeParser::parse_comma_whitespace()
+{
+    if (match(',')) {
+        consume();
+        parse_whitespace();
+    } else {
+        parse_whitespace(1);
+        if (match(','))
+            consume();
+        parse_whitespace();
+    }
+}
+
+float AttributeParser::parse_fractional_constant()
+{
+    StringBuilder builder;
+    bool floating_point = false;
+
+    while (!done() && isdigit(ch()))
+        builder.append(consume());
+
+    if (match('.')) {
+        floating_point = true;
+        builder.append('.');
+        consume();
+        while (!done() && isdigit(ch()))
+            builder.append(consume());
+    } else {
+        VERIFY(builder.length() > 0);
+    }
+
+    if (floating_point)
+        return strtof(builder.to_string().characters(), nullptr);
+    return builder.to_string().to_int().value();
+}
+
+float AttributeParser::parse_number()
+{
+    auto number = parse_fractional_constant();
+
+    if (!match('e') && !match('E'))
+        return number;
+    consume();
+
+    auto exponent_sign = parse_sign();
+
+    StringBuilder exponent_builder;
+    while (!done() && isdigit(ch()))
+        exponent_builder.append(consume());
+    VERIFY(exponent_builder.length() > 0);
+
+    auto exponent = exponent_builder.to_string().to_int().value();
+
+    // Fast path: If the number is 0, there's no point in computing the exponentiation.
+    if (number == 0)
+        return number;
+
+    if (exponent_sign < 0) {
+        for (int i = 0; i < exponent; ++i) {
+            number /= 10;
+        }
+    } else if (exponent_sign > 0) {
+        for (int i = 0; i < exponent; ++i) {
+            number *= 10;
+        }
+    }
+
+    return number;
+}
+
+float AttributeParser::parse_flag()
+{
+    if (!match('0') && !match('1'))
+        VERIFY_NOT_REACHED();
+    return consume() - '0';
+}
+
+int AttributeParser::parse_sign()
+{
+    if (match('-')) {
+        consume();
+        return -1;
+    }
+    if (match('+'))
+        consume();
+    return 1;
+}
+
+bool AttributeParser::match_whitespace() const
+{
+    if (done())
+        return false;
+    char c = ch();
+    return c == 0x9 || c == 0x20 || c == 0xa || c == 0xc || c == 0xd;
+}
+
+bool AttributeParser::match_comma_whitespace() const
+{
+    return match_whitespace() || match(',');
+}
+
+bool AttributeParser::match_coordinate() const
+{
+    return !done() && (isdigit(ch()) || ch() == '-' || ch() == '+' || ch() == '.');
+}
+
+}

+ 85 - 0
Userland/Libraries/LibWeb/SVG/AttributeParser.h

@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org>
+ * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/String.h>
+#include <AK/Vector.h>
+
+namespace Web::SVG {
+
+enum class PathInstructionType {
+    Move,
+    ClosePath,
+    Line,
+    HorizontalLine,
+    VerticalLine,
+    Curve,
+    SmoothCurve,
+    QuadraticBezierCurve,
+    SmoothQuadraticBezierCurve,
+    EllipticalArc,
+    Invalid,
+};
+
+struct PathInstruction {
+    PathInstructionType type;
+    bool absolute;
+    Vector<float> data;
+};
+
+class AttributeParser final {
+public:
+    AttributeParser(String source);
+    ~AttributeParser() = default;
+
+    Vector<PathInstruction> parse_path_data();
+
+private:
+    void parse_drawto();
+
+    void parse_moveto();
+    void parse_closepath();
+    void parse_lineto();
+    void parse_horizontal_lineto();
+    void parse_vertical_lineto();
+    void parse_curveto();
+    void parse_smooth_curveto();
+    void parse_quadratic_bezier_curveto();
+    void parse_smooth_quadratic_bezier_curveto();
+    void parse_elliptical_arc();
+
+    float parse_coordinate();
+    Vector<float> parse_coordinate_pair();
+    Vector<float> parse_coordinate_sequence();
+    Vector<Vector<float>> parse_coordinate_pair_sequence();
+    Vector<float> parse_coordinate_pair_double();
+    Vector<float> parse_coordinate_pair_triplet();
+    Vector<float> parse_elliptical_arg_argument();
+    void parse_whitespace(bool must_match_once = false);
+    void parse_comma_whitespace();
+    float parse_fractional_constant();
+    float parse_number();
+    float parse_flag();
+    // -1 if negative, +1 otherwise
+    int parse_sign();
+
+    bool match_whitespace() const;
+    bool match_comma_whitespace() const;
+    bool match_coordinate() 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++]; }
+
+    String m_source;
+    size_t m_cursor { 0 };
+    Vector<PathInstruction> m_instructions;
+};
+
+}

+ 1 - 356
Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp

@@ -6,14 +6,12 @@
 
 
 #include <AK/Debug.h>
 #include <AK/Debug.h>
 #include <AK/ExtraMathConstants.h>
 #include <AK/ExtraMathConstants.h>
-#include <AK/StringBuilder.h>
 #include <LibGfx/Painter.h>
 #include <LibGfx/Painter.h>
 #include <LibGfx/Path.h>
 #include <LibGfx/Path.h>
 #include <LibWeb/DOM/Document.h>
 #include <LibWeb/DOM/Document.h>
 #include <LibWeb/DOM/Event.h>
 #include <LibWeb/DOM/Event.h>
 #include <LibWeb/Layout/SVGGeometryBox.h>
 #include <LibWeb/Layout/SVGGeometryBox.h>
 #include <LibWeb/SVG/SVGPathElement.h>
 #include <LibWeb/SVG/SVGPathElement.h>
-#include <ctype.h>
 
 
 namespace Web::SVG {
 namespace Web::SVG {
 
 
@@ -85,359 +83,6 @@ namespace Web::SVG {
     }
     }
 }
 }
 
 
-PathDataParser::PathDataParser(const String& source)
-    : m_source(source)
-{
-}
-
-Vector<PathInstruction> PathDataParser::parse()
-{
-    parse_whitespace();
-    while (!done())
-        parse_drawto();
-    if (!m_instructions.is_empty() && m_instructions[0].type != PathInstructionType::Move)
-        VERIFY_NOT_REACHED();
-    return m_instructions;
-}
-
-void PathDataParser::parse_drawto()
-{
-    if (match('M') || match('m')) {
-        parse_moveto();
-    } else if (match('Z') || match('z')) {
-        parse_closepath();
-    } else if (match('L') || match('l')) {
-        parse_lineto();
-    } else if (match('H') || match('h')) {
-        parse_horizontal_lineto();
-    } else if (match('V') || match('v')) {
-        parse_vertical_lineto();
-    } else if (match('C') || match('c')) {
-        parse_curveto();
-    } else if (match('S') || match('s')) {
-        parse_smooth_curveto();
-    } else if (match('Q') || match('q')) {
-        parse_quadratic_bezier_curveto();
-    } else if (match('T') || match('t')) {
-        parse_smooth_quadratic_bezier_curveto();
-    } else if (match('A') || match('a')) {
-        parse_elliptical_arc();
-    } else {
-        dbgln("PathDataParser::parse_drawto failed to match: '{}'", ch());
-        TODO();
-    }
-}
-
-void PathDataParser::parse_moveto()
-{
-    bool absolute = consume() == 'M';
-    parse_whitespace();
-    for (auto& pair : parse_coordinate_pair_sequence())
-        m_instructions.append({ PathInstructionType::Move, absolute, pair });
-}
-
-void PathDataParser::parse_closepath()
-{
-    bool absolute = consume() == 'Z';
-    parse_whitespace();
-    m_instructions.append({ PathInstructionType::ClosePath, absolute, {} });
-}
-
-void PathDataParser::parse_lineto()
-{
-    bool absolute = consume() == 'L';
-    parse_whitespace();
-    for (auto& pair : parse_coordinate_pair_sequence())
-        m_instructions.append({ PathInstructionType::Line, absolute, pair });
-}
-
-void PathDataParser::parse_horizontal_lineto()
-{
-    bool absolute = consume() == 'H';
-    parse_whitespace();
-    m_instructions.append({ PathInstructionType::HorizontalLine, absolute, parse_coordinate_sequence() });
-}
-
-void PathDataParser::parse_vertical_lineto()
-{
-    bool absolute = consume() == 'V';
-    parse_whitespace();
-    m_instructions.append({ PathInstructionType::VerticalLine, absolute, parse_coordinate_sequence() });
-}
-
-void PathDataParser::parse_curveto()
-{
-    bool absolute = consume() == 'C';
-    parse_whitespace();
-
-    while (true) {
-        m_instructions.append({ PathInstructionType::Curve, absolute, parse_coordinate_pair_triplet() });
-        if (match_comma_whitespace())
-            parse_comma_whitespace();
-        if (!match_coordinate())
-            break;
-    }
-}
-
-void PathDataParser::parse_smooth_curveto()
-{
-    bool absolute = consume() == 'S';
-    parse_whitespace();
-
-    while (true) {
-        m_instructions.append({ PathInstructionType::SmoothCurve, absolute, parse_coordinate_pair_double() });
-        if (match_comma_whitespace())
-            parse_comma_whitespace();
-        if (!match_coordinate())
-            break;
-    }
-}
-
-void PathDataParser::parse_quadratic_bezier_curveto()
-{
-    bool absolute = consume() == 'Q';
-    parse_whitespace();
-
-    while (true) {
-        m_instructions.append({ PathInstructionType::QuadraticBezierCurve, absolute, parse_coordinate_pair_double() });
-        if (match_comma_whitespace())
-            parse_comma_whitespace();
-        if (!match_coordinate())
-            break;
-    }
-}
-
-void PathDataParser::parse_smooth_quadratic_bezier_curveto()
-{
-    bool absolute = consume() == 'T';
-    parse_whitespace();
-
-    while (true) {
-        m_instructions.append({ PathInstructionType::SmoothQuadraticBezierCurve, absolute, parse_coordinate_pair() });
-        if (match_comma_whitespace())
-            parse_comma_whitespace();
-        if (!match_coordinate())
-            break;
-    }
-}
-
-void PathDataParser::parse_elliptical_arc()
-{
-    bool absolute = consume() == 'A';
-    parse_whitespace();
-
-    while (true) {
-        m_instructions.append({ PathInstructionType::EllipticalArc, absolute, parse_elliptical_arg_argument() });
-        if (match_comma_whitespace())
-            parse_comma_whitespace();
-        if (!match_coordinate())
-            break;
-    }
-}
-
-float PathDataParser::parse_coordinate()
-{
-    return parse_sign() * parse_number();
-}
-
-Vector<float> PathDataParser::parse_coordinate_pair()
-{
-    Vector<float> coordinates;
-    coordinates.append(parse_coordinate());
-    if (match_comma_whitespace())
-        parse_comma_whitespace();
-    coordinates.append(parse_coordinate());
-    return coordinates;
-}
-
-Vector<float> PathDataParser::parse_coordinate_sequence()
-{
-    Vector<float> sequence;
-    while (true) {
-        sequence.append(parse_coordinate());
-        if (match_comma_whitespace())
-            parse_comma_whitespace();
-        if (!match_comma_whitespace() && !match_coordinate())
-            break;
-    }
-    return sequence;
-}
-
-Vector<Vector<float>> PathDataParser::parse_coordinate_pair_sequence()
-{
-    Vector<Vector<float>> sequence;
-    while (true) {
-        sequence.append(parse_coordinate_pair());
-        if (match_comma_whitespace())
-            parse_comma_whitespace();
-        if (!match_comma_whitespace() && !match_coordinate())
-            break;
-    }
-    return sequence;
-}
-
-Vector<float> PathDataParser::parse_coordinate_pair_double()
-{
-    Vector<float> coordinates;
-    coordinates.extend(parse_coordinate_pair());
-    if (match_comma_whitespace())
-        parse_comma_whitespace();
-    coordinates.extend(parse_coordinate_pair());
-    return coordinates;
-}
-
-Vector<float> PathDataParser::parse_coordinate_pair_triplet()
-{
-    Vector<float> coordinates;
-    coordinates.extend(parse_coordinate_pair());
-    if (match_comma_whitespace())
-        parse_comma_whitespace();
-    coordinates.extend(parse_coordinate_pair());
-    if (match_comma_whitespace())
-        parse_comma_whitespace();
-    coordinates.extend(parse_coordinate_pair());
-    return coordinates;
-}
-
-Vector<float> PathDataParser::parse_elliptical_arg_argument()
-{
-    Vector<float> numbers;
-    numbers.append(parse_number());
-    if (match_comma_whitespace())
-        parse_comma_whitespace();
-    numbers.append(parse_number());
-    if (match_comma_whitespace())
-        parse_comma_whitespace();
-    numbers.append(parse_number());
-    parse_comma_whitespace();
-    numbers.append(parse_flag());
-    if (match_comma_whitespace())
-        parse_comma_whitespace();
-    numbers.append(parse_flag());
-    if (match_comma_whitespace())
-        parse_comma_whitespace();
-    numbers.extend(parse_coordinate_pair());
-
-    return numbers;
-}
-
-void PathDataParser::parse_whitespace(bool must_match_once)
-{
-    bool matched = false;
-    while (!done() && match_whitespace()) {
-        consume();
-        matched = true;
-    }
-
-    VERIFY(!must_match_once || matched);
-}
-
-void PathDataParser::parse_comma_whitespace()
-{
-    if (match(',')) {
-        consume();
-        parse_whitespace();
-    } else {
-        parse_whitespace(1);
-        if (match(','))
-            consume();
-        parse_whitespace();
-    }
-}
-
-float PathDataParser::parse_fractional_constant()
-{
-    StringBuilder builder;
-    bool floating_point = false;
-
-    while (!done() && isdigit(ch()))
-        builder.append(consume());
-
-    if (match('.')) {
-        floating_point = true;
-        builder.append('.');
-        consume();
-        while (!done() && isdigit(ch()))
-            builder.append(consume());
-    } else {
-        VERIFY(builder.length() > 0);
-    }
-
-    if (floating_point)
-        return strtof(builder.to_string().characters(), nullptr);
-    return builder.to_string().to_int().value();
-}
-
-float PathDataParser::parse_number()
-{
-    auto number = parse_fractional_constant();
-
-    if (!match('e') && !match('E'))
-        return number;
-    consume();
-
-    auto exponent_sign = parse_sign();
-
-    StringBuilder exponent_builder;
-    while (!done() && isdigit(ch()))
-        exponent_builder.append(consume());
-    VERIFY(exponent_builder.length() > 0);
-
-    auto exponent = exponent_builder.to_string().to_int().value();
-
-    // Fast path: If the number is 0, there's no point in computing the exponentiation.
-    if (number == 0)
-        return number;
-
-    if (exponent_sign < 0) {
-        for (int i = 0; i < exponent; ++i) {
-            number /= 10;
-        }
-    } else if (exponent_sign > 0) {
-        for (int i = 0; i < exponent; ++i) {
-            number *= 10;
-        }
-    }
-
-    return number;
-}
-
-float PathDataParser::parse_flag()
-{
-    if (!match('0') && !match('1'))
-        VERIFY_NOT_REACHED();
-    return consume() - '0';
-}
-
-int PathDataParser::parse_sign()
-{
-    if (match('-')) {
-        consume();
-        return -1;
-    }
-    if (match('+'))
-        consume();
-    return 1;
-}
-
-bool PathDataParser::match_whitespace() const
-{
-    if (done())
-        return false;
-    char c = ch();
-    return c == 0x9 || c == 0x20 || c == 0xa || c == 0xc || c == 0xd;
-}
-
-bool PathDataParser::match_comma_whitespace() const
-{
-    return match_whitespace() || match(',');
-}
-
-bool PathDataParser::match_coordinate() const
-{
-    return !done() && (isdigit(ch()) || ch() == '-' || ch() == '+' || ch() == '.');
-}
-
 SVGPathElement::SVGPathElement(DOM::Document& document, QualifiedName qualified_name)
 SVGPathElement::SVGPathElement(DOM::Document& document, QualifiedName qualified_name)
     : SVGGeometryElement(document, move(qualified_name))
     : SVGGeometryElement(document, move(qualified_name))
 {
 {
@@ -448,7 +93,7 @@ void SVGPathElement::parse_attribute(const FlyString& name, const String& value)
     SVGGeometryElement::parse_attribute(name, value);
     SVGGeometryElement::parse_attribute(name, value);
 
 
     if (name == "d")
     if (name == "d")
-        m_instructions = PathDataParser(value).parse();
+        m_instructions = AttributeParser(value).parse_path_data();
 }
 }
 
 
 Gfx::Path& SVGPathElement::get_path()
 Gfx::Path& SVGPathElement::get_path()

+ 1 - 70
Userland/Libraries/LibWeb/SVG/SVGPathElement.h

@@ -8,80 +8,11 @@
 
 
 #include <LibGfx/Bitmap.h>
 #include <LibGfx/Bitmap.h>
 #include <LibWeb/HTML/HTMLElement.h>
 #include <LibWeb/HTML/HTMLElement.h>
+#include <LibWeb/SVG/AttributeParser.h>
 #include <LibWeb/SVG/SVGGeometryElement.h>
 #include <LibWeb/SVG/SVGGeometryElement.h>
 
 
 namespace Web::SVG {
 namespace Web::SVG {
 
 
-enum class PathInstructionType {
-    Move,
-    ClosePath,
-    Line,
-    HorizontalLine,
-    VerticalLine,
-    Curve,
-    SmoothCurve,
-    QuadraticBezierCurve,
-    SmoothQuadraticBezierCurve,
-    EllipticalArc,
-    Invalid,
-};
-
-struct PathInstruction {
-    PathInstructionType type;
-    bool absolute;
-    Vector<float> data;
-};
-
-class PathDataParser final {
-public:
-    PathDataParser(const String& source);
-    ~PathDataParser() = default;
-
-    Vector<PathInstruction> parse();
-
-private:
-    void parse_drawto();
-
-    void parse_moveto();
-    void parse_closepath();
-    void parse_lineto();
-    void parse_horizontal_lineto();
-    void parse_vertical_lineto();
-    void parse_curveto();
-    void parse_smooth_curveto();
-    void parse_quadratic_bezier_curveto();
-    void parse_smooth_quadratic_bezier_curveto();
-    void parse_elliptical_arc();
-
-    float parse_coordinate();
-    Vector<float> parse_coordinate_pair();
-    Vector<float> parse_coordinate_sequence();
-    Vector<Vector<float>> parse_coordinate_pair_sequence();
-    Vector<float> parse_coordinate_pair_double();
-    Vector<float> parse_coordinate_pair_triplet();
-    Vector<float> parse_elliptical_arg_argument();
-    void parse_whitespace(bool must_match_once = false);
-    void parse_comma_whitespace();
-    float parse_fractional_constant();
-    float parse_number();
-    float parse_flag();
-    // -1 if negative, +1 otherwise
-    int parse_sign();
-
-    bool match_whitespace() const;
-    bool match_comma_whitespace() const;
-    bool match_coordinate() 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++]; }
-
-    String m_source;
-    size_t m_cursor { 0 };
-    Vector<PathInstruction> m_instructions;
-};
-
 class SVGPathElement final : public SVGGeometryElement {
 class SVGPathElement final : public SVGGeometryElement {
 public:
 public:
     using WrapperType = Bindings::SVGPathElementWrapper;
     using WrapperType = Bindings::SVGPathElementWrapper;