Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsvgcssproperties.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
6
8
9using namespace Qt::Literals::StringLiterals;
10
11namespace {
12
13int parseCssClockValue(QStringView str, bool *ok)
14{
15 int res = 0;
16 int ms = 1000;
17 str = str.trimmed();
18 if (str.endsWith("ms"_L1)) {
19 str.chop(2);
20 ms = 1;
21 } else if (str.endsWith("s"_L1)) {
22 str.chop(1);
23 } else {
24 if (ok)
25 *ok = false;
26 return res;
27 }
28 double val = ms * str.toDouble(ok);
29 if (ok) {
30 if (val > std::numeric_limits<int>::min() && val < std::numeric_limits<int>::max())
31 res = static_cast<int>(val);
32 else
33 *ok = false;
34 }
35 return res;
36}
37
38bool isTimeValue(QStringView str) {
39 if (str.endsWith("ms"_L1))
40 str.chop(2);
41 else if (str.endsWith("s"_L1))
42 str.chop(1);
43 else
44 return false;
45
46 bool ok;
47 Q_UNUSED(str.toDouble(&ok))
48 return ok;
49}
50
51bool isIteration(QStringView str) {
52 if (str == "infinite"_L1)
53 return true;
54 bool ok;
55 Q_UNUSED(str.toDouble(&ok))
56 return ok;
57}
58
59bool isDirection(QStringView str) {
60 return str == "normal"_L1 || str == "reverse"_L1 || str.startsWith("alternate"_L1);
61}
62
63bool isTimingFunction(QStringView str) {
64 return str.startsWith("linear"_L1) || str.startsWith("ease"_L1)
65 || str.startsWith("step"_L1) || str.startsWith("cubic-bezier"_L1);
66}
67
68bool isFillMode(QStringView str) {
69 return str == "none"_L1 || str == "forwards"_L1 || str == "backwards"_L1 || str == "both"_L1;
70}
71
72bool isPlayState(QStringView str) {
73 return str == "paused"_L1 || str =="running"_L1;
74}
75
76}
77
78QSvgCssAnimationProperties::QSvgCssAnimationProperties(const QXmlStreamAttributes &attributes)
79{
80 QRegularExpression re(";| "_L1);
81 for (int i = 0; i < attributes.size(); ++i) {
82 const QXmlStreamAttribute &attribute = attributes.at(i);
83 QStringView name = attribute.qualifiedName();
84 if (name.isEmpty())
85 continue;
86 QStringView value = attribute.value();
87
88 switch (name.at(0).unicode()) {
89
90 case 'a':
91 if (name == "animation"_L1)
92 shortHandtoLonghandForm(value);
93 if (name == "animation-name"_L1)
94 m_names = value.split(re, Qt::SkipEmptyParts);
95 if (name == "animation-duration"_L1)
96 m_durations = value.split(re, Qt::SkipEmptyParts);
97 if (name == "animation-delay"_L1)
98 m_delays = value.split(re, Qt::SkipEmptyParts);
99 if (name == "animation-iteration-count"_L1)
100 m_iterationCounts = value.split(re, Qt::SkipEmptyParts);
101 if (name == "animation-direction"_L1)
102 m_directions = value.split(re, Qt::SkipEmptyParts);
103 if (name == "animation-timing-function"_L1)
104 m_timingFunctions = value.split(re, Qt::SkipEmptyParts);
105 if (name == "animation-fill-mode"_L1)
106 m_fillModes = value.split(re, Qt::SkipEmptyParts);
107 if (name == "animation-play-state"_L1)
108 m_playStates = value.split(re, Qt::SkipEmptyParts);
109 break;
110
111 default:
112 break;
113 }
114 }
115}
116
118{
119 bool ok;
120 QList<QSvgAnimationProperty> parsedProperties;
121 for (int i = 0; i < m_names.size(); i++) {
122 QSvgAnimationProperty property;
123 property.name = m_names.at(i);
124
125 if (!m_durations.isEmpty()) {
126 QStringView durationStr = m_durations.at(i % m_durations.size());
127 int duration = parseCssClockValue(durationStr, &ok);
128 property.duration = ok ? duration : 0;
129 }
130
131 if (!m_delays.isEmpty()) {
132 QStringView delayStr = m_delays.at(i % m_delays.size());
133 int delay = parseCssClockValue(delayStr, &ok);
134 property.delay = ok ? delay : 0;
135 }
136
137 if (!m_iterationCounts.isEmpty()) {
138 QStringView iterationStr = m_iterationCounts.at(i % m_iterationCounts.size());
139 int iteration;
140 if (iterationStr == "infinite"_L1) {
141 iteration = -1;
142 } else {
143 qreal count = iterationStr.toDouble(&ok);
144 iteration = ok ? qMax(1.0, count) : 0;
145 }
146 property.iteration = iteration;
147 }
148
149 parsedProperties.append(property);
150 }
151
152 return parsedProperties;
153}
154
155void QSvgCssAnimationProperties::shortHandtoLonghandForm(QStringView value)
156{
157 m_names.clear();
158 m_durations.clear();
159 m_delays.clear();
160 m_iterationCounts.clear();
161 m_directions.clear();
162 m_timingFunctions.clear();
163 m_fillModes.clear();
164 m_playStates.clear();
165
166 enum Property : uchar {
167 Duration = 1,
168 Delay = 1 << 1,
169 IterationCount = 1 << 2,
170 Direction = 1 << 3,
171 TimingFunction = 1 << 4,
172 FillMode = 1 << 5,
173 PlayState = 1 << 6,
174 Name = 1 << 7
175 };
176
177 QList<QStringView> animations = value.split(QLatin1Char(';'), Qt::SkipEmptyParts);
178 for (QStringView animation : animations) {
179 uchar propertyFlag = 0;
180 QList<QStringView> animationProperties = animation.split(QLatin1Char(' '), Qt::SkipEmptyParts);
181 for (QStringView property : animationProperties) {
182 if (!(propertyFlag & Property::Duration) && isTimeValue(property)) {
183 m_durations.append(property);
184 propertyFlag |= Property::Duration;
185 } else if (!(propertyFlag & Property::Delay) && isTimeValue(property)) {
186 m_delays.append(property);
187 propertyFlag |= Property::Delay;
188 } else if (!(propertyFlag & Property::IterationCount) && isIteration(property)) {
189 m_iterationCounts.append(property);
190 propertyFlag |= Property::IterationCount;
191 } else if (!(propertyFlag & Property::Direction) && isDirection(property)) {
192 m_directions.append(property);
193 propertyFlag |= Property::Direction;
194 } else if (!(propertyFlag & Property::TimingFunction) && isTimingFunction(property)) {
195 m_timingFunctions.append(property);
196 propertyFlag |= Property::TimingFunction;
197 } else if (!(propertyFlag & Property::FillMode) && isFillMode(property)) {
198 m_fillModes.append(property);
199 propertyFlag |= Property::FillMode;
200 } else if (!(propertyFlag & Property::PlayState)&& isPlayState(property)) {
201 m_playStates.append(property);
202 propertyFlag |= Property::PlayState;
203 } else {
204 m_names.append(property);
205 propertyFlag |= Property::Name;
206 }
207 }
208 }
209}
210
211QT_END_NAMESPACE
QList< QSvgAnimationProperty > parse() const
QSvgCssAnimationProperties(const QXmlStreamAttributes &attributes)