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#include <QtSvg/private/qsvgutils_p.h>
7
8
10
11using namespace Qt::Literals::StringLiterals;
12using namespace QSvgCssValues;
13
14namespace {
15
16int parseCssClockValue(QStringView str, bool *ok)
17{
18 int res = 0;
19 int ms = 1000;
20 str = str.trimmed();
21 if (str.endsWith("ms"_L1)) {
22 str.chop(2);
23 ms = 1;
24 } else if (str.endsWith("s"_L1)) {
25 str.chop(1);
26 } else {
27 if (ok)
28 *ok = false;
29 return res;
30 }
31 double val = ms * str.toDouble(ok);
32 if (ok) {
33 if (val > std::numeric_limits<int>::min() && val < std::numeric_limits<int>::max())
34 res = static_cast<int>(val);
35 else
36 *ok = false;
37 }
38 return res;
39}
40
41bool isTimeValue(QStringView str) {
42 if (str.endsWith("ms"_L1))
43 str.chop(2);
44 else if (str.endsWith("s"_L1))
45 str.chop(1);
46 else
47 return false;
48
49 bool ok;
50 Q_UNUSED(str.toDouble(&ok))
51 return ok;
52}
53
54bool isIteration(QStringView str) {
55 if (str == "infinite"_L1)
56 return true;
57 bool ok;
58 Q_UNUSED(str.toDouble(&ok))
59 return ok;
60}
61
62bool isDirection(QStringView str) {
63 return str == "normal"_L1 || str == "reverse"_L1 || str.startsWith("alternate"_L1);
64}
65
66bool isTimingFunction(QStringView str) {
67 return str.startsWith("linear"_L1) || str.startsWith("ease"_L1)
68 || str.startsWith("step"_L1) || str.startsWith("cubic-bezier"_L1);
69}
70
71bool isFillMode(QStringView str) {
72 return str == "none"_L1 || str == "forwards"_L1 || str == "backwards"_L1 || str == "both"_L1;
73}
74
75bool isPlayState(QStringView str) {
76 return str == "paused"_L1 || str =="running"_L1;
77}
78
79}
80
81QSvgCssProperties::QSvgCssProperties(const QXmlStreamAttributes &attributes)
82{
83 QRegularExpression re(";| "_L1);
84 for (int i = 0; i < attributes.size(); ++i) {
85 const QXmlStreamAttribute &attribute = attributes.at(i);
86 QStringView name = attribute.qualifiedName();
87 if (name.isEmpty())
88 continue;
89 QStringView value = attribute.value();
90 switch (name.at(0).unicode()) {
91
92 case 'a':
93 if (name == "animation"_L1)
94 shortHandtoLonghandForm(value);
95 if (name == "animation-name"_L1)
96 m_names = value.split(re, Qt::SkipEmptyParts);
97 if (name == "animation-duration"_L1)
98 m_durations = value.split(re, Qt::SkipEmptyParts);
99 if (name == "animation-delay"_L1)
100 m_delays = value.split(re, Qt::SkipEmptyParts);
101 if (name == "animation-iteration-count"_L1)
102 m_iterationCounts = value.split(re, Qt::SkipEmptyParts);
103 if (name == "animation-direction"_L1)
104 m_directions = value.split(re, Qt::SkipEmptyParts);
105 if (name == "animation-timing-function"_L1)
106 m_timingFunctions = value.split(re, Qt::SkipEmptyParts);
107 if (name == "animation-fill-mode"_L1)
108 m_fillModes = value.split(re, Qt::SkipEmptyParts);
109 if (name == "animation-play-state"_L1)
110 m_playStates = value.split(re, Qt::SkipEmptyParts);
111 break;
112
113 case 'o':
114 if (name == "offset-path"_L1)
115 m_offsetPath = value;
116 if (name == "offset-distance"_L1)
117 m_offsetDistance = value;
118 if (name == "offset-rotate"_L1)
119 m_offsetRotate = value;
120 break;
121
122 default:
123 break;
124 }
125 }
126}
127
129{
130 bool ok;
131 QList<QSvgAnimationProperty> parsedProperties;
132 for (int i = 0; i < m_names.size(); i++) {
133 QSvgAnimationProperty property;
134 property.name = m_names.at(i);
135
136 if (!m_durations.isEmpty()) {
137 QStringView durationStr = m_durations.at(i % m_durations.size());
138 int duration = parseCssClockValue(durationStr, &ok);
139 property.duration = ok ? duration : 0;
140 }
141
142 if (!m_delays.isEmpty()) {
143 QStringView delayStr = m_delays.at(i % m_delays.size());
144 int delay = parseCssClockValue(delayStr, &ok);
145 property.delay = ok ? delay : 0;
146 }
147
148 if (!m_iterationCounts.isEmpty()) {
149 QStringView iterationStr = m_iterationCounts.at(i % m_iterationCounts.size());
150 int iteration;
151 if (iterationStr == "infinite"_L1) {
152 iteration = -1;
153 } else {
154 qreal count = iterationStr.toDouble(&ok);
155 iteration = ok ? qMax(1.0, count) : 0;
156 }
157 property.iteration = iteration;
158 }
159
160 if (!m_timingFunctions.isEmpty()) {
161 QStringView timingFunctionStr = m_timingFunctions.at(i % m_timingFunctions.size());
162 if (timingFunctionStr == "linear"_L1) {
163 property.easingFunction = EasingFunction::Linear;
164 } else if (timingFunctionStr == "ease-in"_L1) {
165 property.easingFunction = EasingFunction::EaseIn;
166 } else if (timingFunctionStr == "ease-out"_L1) {
167 property.easingFunction = EasingFunction::EaseOut;
168 } else if (timingFunctionStr == "ease-in-out"_L1) {
169 property.easingFunction = EasingFunction::EaseInOut;
170 } else if (timingFunctionStr == "step-end"_L1) {
171 property.easingFunction = EasingFunction::Steps;
172 property.easingValues = StepValues{quint32(1), QSvgCssValues::StepPosition::End};
173 } else if (timingFunctionStr == "step-start"_L1) {
174 property.easingFunction = EasingFunction::Steps;
175 property.easingValues = StepValues{quint32(1), QSvgCssValues::StepPosition::Start};
176 }
177 }
178 parsedProperties.append(property);
179 }
180
181 return parsedProperties;
182}
183
185{
186 QSvgOffsetProperty offset;
187
188 offset.path = QSvgUtils::parsePathDataFast(m_offsetPath);
189
190 qsizetype index;
191 Qt::CaseSensitivity cs = Qt::CaseInsensitive;
192 if ((index = m_offsetRotate.indexOf("auto"_L1, 0, cs)) >= 0) {
193 std::optional<qreal> angle = QSvgUtils::parseAngle(m_offsetRotate.sliced(index + 4));
194 offset.rotateType = angle ? QtSvg::OffsetRotateType::AutoAngle :
195 QtSvg::OffsetRotateType::Auto;
196 offset.angle = angle.value_or(0);
197 } else if ((index = m_offsetRotate.indexOf("reverse"_L1, 0, cs)) >= 0) {
198 std::optional<qreal> angle = QSvgUtils::parseAngle(m_offsetRotate.sliced(index + 7));
199 offset.rotateType = angle ? QtSvg::OffsetRotateType::ReverseAngle :
200 QtSvg::OffsetRotateType::Reverse;
201 offset.angle = angle.value_or(0);
202 } else {
203 std::optional<qreal> angle = QSvgUtils::parseAngle(m_offsetRotate);
204 offset.rotateType = angle ? QtSvg::OffsetRotateType::Angle :
205 QtSvg::OffsetRotateType::Auto;
206 offset.angle = angle.value_or(0);
207 }
208
209 QSvgUtils::LengthType type;
210 bool *ok = nullptr;
211 offset.distance= QSvgUtils::parseLength(m_offsetDistance, &type, ok);
212 if (type == QSvgUtils::LengthType::LT_PERCENT) {
213 offset.distance = offset.distance / 100.0;
214 }
215
216 return offset;
217}
218
219void QSvgCssProperties::shortHandtoLonghandForm(QStringView value)
220{
221 m_names.clear();
222 m_durations.clear();
223 m_delays.clear();
224 m_iterationCounts.clear();
225 m_directions.clear();
226 m_timingFunctions.clear();
227 m_fillModes.clear();
228 m_playStates.clear();
229
230 enum Property : uchar {
231 Duration = 1,
232 Delay = 1 << 1,
233 IterationCount = 1 << 2,
234 Direction = 1 << 3,
235 TimingFunction = 1 << 4,
236 FillMode = 1 << 5,
237 PlayState = 1 << 6,
238 Name = 1 << 7
239 };
240
241 QList<QStringView> animations = value.split(QLatin1Char(';'), Qt::SkipEmptyParts);
242 for (QStringView animation : animations) {
243 uchar propertyFlag = 0;
244 QList<QStringView> animationProperties = animation.split(QLatin1Char(' '), Qt::SkipEmptyParts);
245 for (QStringView property : animationProperties) {
246 if (!(propertyFlag & Property::Duration) && isTimeValue(property)) {
247 m_durations.append(property);
248 propertyFlag |= Property::Duration;
249 } else if (!(propertyFlag & Property::Delay) && isTimeValue(property)) {
250 m_delays.append(property);
251 propertyFlag |= Property::Delay;
252 } else if (!(propertyFlag & Property::IterationCount) && isIteration(property)) {
253 m_iterationCounts.append(property);
254 propertyFlag |= Property::IterationCount;
255 } else if (!(propertyFlag & Property::Direction) && isDirection(property)) {
256 m_directions.append(property);
257 propertyFlag |= Property::Direction;
258 } else if (!(propertyFlag & Property::TimingFunction) && isTimingFunction(property)) {
259 m_timingFunctions.append(property);
260 propertyFlag |= Property::TimingFunction;
261 } else if (!(propertyFlag & Property::FillMode) && isFillMode(property)) {
262 m_fillModes.append(property);
263 propertyFlag |= Property::FillMode;
264 } else if (!(propertyFlag & Property::PlayState)&& isPlayState(property)) {
265 m_playStates.append(property);
266 propertyFlag |= Property::PlayState;
267 } else {
268 m_names.append(property);
269 propertyFlag |= Property::Name;
270 }
271 }
272 }
273}
274
275QT_END_NAMESPACE
QSvgOffsetProperty offset() const
QSvgCssProperties(const QXmlStreamAttributes &attributes)
QList< QSvgAnimationProperty > animations() const
Combined button and popup list for selecting options.