Coverage Report

Created: 2026-02-14 08:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibWeb/SVG/AttributeParser.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org>
3
 * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
4
 * Copyright (c) 2024, Tim Ledbetter <timledbetter@gmail.com>
5
 *
6
 * SPDX-License-Identifier: BSD-2-Clause
7
 */
8
9
#pragma once
10
11
#include <AK/GenericLexer.h>
12
#include <AK/Variant.h>
13
#include <AK/Vector.h>
14
#include <LibGfx/Point.h>
15
16
namespace Web::SVG {
17
18
enum class PathInstructionType {
19
    Move,
20
    ClosePath,
21
    Line,
22
    HorizontalLine,
23
    VerticalLine,
24
    Curve,
25
    SmoothCurve,
26
    QuadraticBezierCurve,
27
    SmoothQuadraticBezierCurve,
28
    EllipticalArc,
29
    Invalid,
30
};
31
32
struct PathInstruction {
33
    PathInstructionType type;
34
    bool absolute;
35
    Vector<float> data;
36
};
37
38
struct Transform {
39
    struct Translate {
40
        float x;
41
        float y;
42
    };
43
    struct Scale {
44
        float x;
45
        float y;
46
    };
47
    struct Rotate {
48
        float a;
49
        float x;
50
        float y;
51
    };
52
    struct SkewX {
53
        float a;
54
    };
55
    struct SkewY {
56
        float a;
57
    };
58
    struct Matrix {
59
        float a;
60
        float b;
61
        float c;
62
        float d;
63
        float e;
64
        float f;
65
    };
66
67
    using Operation = Variant<Translate, Scale, Rotate, SkewX, SkewY, Matrix>;
68
    Operation operation;
69
};
70
71
struct PreserveAspectRatio {
72
    enum class Align {
73
        None,
74
        xMinYMin,
75
        xMidYMin,
76
        xMaxYMin,
77
        xMinYMid,
78
        xMidYMid,
79
        xMaxYMid,
80
        xMinYMax,
81
        xMidYMax,
82
        xMaxYMax
83
    };
84
    enum class MeetOrSlice {
85
        Meet,
86
        Slice
87
    };
88
    Align align { Align::xMidYMid };
89
    MeetOrSlice meet_or_slice { MeetOrSlice::Meet };
90
};
91
92
enum class SVGUnits {
93
    ObjectBoundingBox,
94
    UserSpaceOnUse
95
};
96
97
using GradientUnits = SVGUnits;
98
using MaskUnits = SVGUnits;
99
using MaskContentUnits = SVGUnits;
100
using ClipPathUnits = SVGUnits;
101
102
enum class SpreadMethod {
103
    Pad,
104
    Repeat,
105
    Reflect
106
};
107
108
class NumberPercentage {
109
public:
110
    NumberPercentage(float value, bool is_percentage)
111
0
        : m_value(is_percentage ? value / 100 : value)
112
0
        , m_is_percentage(is_percentage)
113
0
    {
114
0
    }
115
116
    static NumberPercentage create_percentage(float value)
117
0
    {
118
0
        return NumberPercentage(value, true);
119
0
    }
120
121
    static NumberPercentage create_number(float value)
122
0
    {
123
0
        return NumberPercentage(value, false);
124
0
    }
125
126
    float resolve_relative_to(float length) const;
127
128
0
    float value() const { return m_value; }
129
130
private:
131
    float m_value;
132
    bool m_is_percentage { false };
133
};
134
135
enum class FillRule {
136
    Nonzero,
137
    Evenodd
138
};
139
140
using ClipRule = FillRule;
141
142
enum class TextAnchor {
143
    Start,
144
    Middle,
145
    End
146
};
147
148
class AttributeParser final {
149
public:
150
0
    ~AttributeParser() = default;
151
152
    static Optional<float> parse_coordinate(StringView input);
153
    static Optional<float> parse_length(StringView input);
154
    static Optional<NumberPercentage> parse_number_percentage(StringView input);
155
    static Optional<float> parse_positive_length(StringView input);
156
    static Vector<Gfx::FloatPoint> parse_points(StringView input);
157
    static Vector<PathInstruction> parse_path_data(StringView input);
158
    static Optional<Vector<Transform>> parse_transform(StringView input);
159
    static Optional<PreserveAspectRatio> parse_preserve_aspect_ratio(StringView input);
160
    static Optional<SVGUnits> parse_units(StringView input);
161
    static Optional<SpreadMethod> parse_spread_method(StringView input);
162
163
private:
164
    AttributeParser(StringView source);
165
166
    ErrorOr<void> parse_drawto();
167
    ErrorOr<void> parse_moveto();
168
    void parse_closepath();
169
    ErrorOr<void> parse_lineto();
170
    ErrorOr<void> parse_horizontal_lineto();
171
    ErrorOr<void> parse_vertical_lineto();
172
    ErrorOr<void> parse_curveto();
173
    ErrorOr<void> parse_smooth_curveto();
174
    ErrorOr<void> parse_quadratic_bezier_curveto();
175
    ErrorOr<void> parse_smooth_quadratic_bezier_curveto();
176
    ErrorOr<void> parse_elliptical_arc();
177
178
    Optional<Vector<Transform>> parse_transform();
179
180
    ErrorOr<float> parse_length();
181
    ErrorOr<float> parse_coordinate();
182
    ErrorOr<Vector<float>> parse_coordinate_pair();
183
    ErrorOr<Vector<float>> parse_coordinate_sequence();
184
    ErrorOr<Vector<Vector<float>>> parse_coordinate_pair_sequence();
185
    ErrorOr<Vector<float>> parse_coordinate_pair_double();
186
    ErrorOr<Vector<float>> parse_coordinate_pair_triplet();
187
    ErrorOr<Vector<float>> parse_elliptical_arc_argument();
188
    void parse_whitespace(bool must_match_once = false);
189
    void parse_comma_whitespace();
190
    ErrorOr<float> parse_number();
191
    ErrorOr<float> parse_nonnegative_number();
192
    ErrorOr<float> parse_flag();
193
    // -1 if negative, +1 otherwise
194
    int parse_sign();
195
196
    bool match_whitespace() const;
197
    bool match_comma_whitespace() const;
198
    bool match_coordinate() const;
199
    bool match_length() const;
200
    bool match_number() const;
201
0
    bool match(char c) const { return !done() && ch() == c; }
202
203
0
    bool done() const { return m_lexer.is_eof(); }
204
0
    char ch(size_t offset = 0) const { return m_lexer.peek(offset); }
205
0
    char consume() { return m_lexer.consume(); }
206
207
    GenericLexer m_lexer;
208
    Vector<PathInstruction> m_instructions;
209
};
210
211
}