22bool fillPropertyEasing(
const QList<CssKeyFrameValue> &keyFrames, QSvgAbstractAnimatedProperty *prop)
24 for (
const CssKeyFrameValue &keyFrameValue : keyFrames) {
25 QStringView timingFunctionStr = QSvgCssHandler::parseDecltoString(keyFrameValue.timingFunction);
26 QSvgEasingInterfacePtr easing;
27 if (timingFunctionStr == QStringLiteral(
"linear")) {
28 easing = QSvgCssHandler::createEasing(QSvgCssValues::EasingFunction::Linear);
29 }
else if (timingFunctionStr == QStringLiteral(
"ease-in")) {
30 easing = QSvgCssHandler::createEasing(QSvgCssValues::EasingFunction::EaseIn);
31 }
else if (timingFunctionStr == QStringLiteral(
"ease-out")) {
32 easing = QSvgCssHandler::createEasing(QSvgCssValues::EasingFunction::EaseOut);
33 }
else if (timingFunctionStr == QStringLiteral(
"ease-in-out")) {
34 easing = QSvgCssHandler::createEasing(QSvgCssValues::EasingFunction::EaseInOut);
35 }
else if (timingFunctionStr == QStringLiteral(
"step-end")) {
36 easing = QSvgCssHandler::createEasing(QSvgCssValues::EasingFunction::Steps,
37 QSvgCssValues::StepValues{quint32(1),
38 QSvgCssValues::StepPosition::End});
39 }
else if (timingFunctionStr == QStringLiteral(
"step-start")) {
40 easing = QSvgCssHandler::createEasing(QSvgCssValues::EasingFunction::Steps,
41 QSvgCssValues::StepValues{quint32(1),
42 QSvgCssValues::StepPosition::Start});
45 prop->appendEasing(std::move(easing));
82bool validateTransform(QList<QList<QSvgAnimatedPropertyTransform::TransformComponent>> &keyFrameComponents) {
84 if (keyFrameComponents.size() < 2)
87 qsizetype maxIndex = 0;
88 qsizetype maxSize = 0;
89 for (
int i = 1; i < keyFrameComponents.size(); i++) {
90 auto &listA = keyFrameComponents[i - 1];
91 auto &listB = keyFrameComponents[i];
92 for (
int j = 0; j < qMin(listA.size(), listB.size()); j++) {
93 auto typeA = listA.at(j).type;
94 auto typeB = listB.at(j).type;
100 if (listA.size() > maxSize) {
102 maxSize = listA.size();
105 if (listB.size() > maxSize) {
107 maxSize = listB.size();
111 const auto &longList = keyFrameComponents.at(maxIndex);
113 for (
auto &list : keyFrameComponents) {
114 qsizetype size = list.size();
116 for (
int j = size; j < maxSize; j++) {
117 QSvgAnimatedPropertyTransform::TransformComponent comp = longList.value(j);
119 case QSvgAnimatedPropertyTransform::TransformComponent::Translate:
120 case QSvgAnimatedPropertyTransform::TransformComponent::Skew:
124 case QSvgAnimatedPropertyTransform::TransformComponent::Rotate:
129 case QSvgAnimatedPropertyTransform::TransformComponent::Scale:
146 QList<QList<QSvgAnimatedPropertyTransform::TransformComponent>> keyFramesComponents;
148 for (
const CssKeyFrameValue &keyFrame : keyFrames) {
149 QList<QSvgAnimatedPropertyTransform::TransformComponent> components;
150 for (
const QCss::Value &val : keyFrame.values) {
151 if (val.type == QCss::Value::Function) {
152 const QStringList lst = val.variant.toStringList();
153 const QStringView transformType = lst.value(0);
154 const QList<QStringView> args =
155 QStringView{ lst.value(1) }.split(QStringLiteral(
","), Qt::SkipEmptyParts);
156 if (transformType == QStringLiteral(
"scale")) {
157 QSvgAnimatedPropertyTransform::TransformComponent component;
158 qreal scale0 = QSvgUtils::toDouble(args.value(0).trimmed());
159 qreal scale1 = QSvgUtils::toDouble(args.value(1).trimmed());
160 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Scale;
161 component.values.append(scale0);
162 component.values.append(scale1);
163 components.append(component);
164 }
else if (transformType == QStringLiteral(
"translate")) {
165 QSvgAnimatedPropertyTransform::TransformComponent component;
166 QSvgUtils::LengthType type;
167 qreal translate0 = QSvgUtils::parseLength(args.value(0), &type);
168 translate0 = QSvgUtils::convertToPixels(translate0,
false, type);
169 qreal translate1 = QSvgUtils::parseLength(args.value(1), &type);
170 translate1 = QSvgUtils::convertToPixels(translate1,
false, type);
171 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Translate;
172 component.values.append(translate0);
173 component.values.append(translate1);
174 components.append(component);
175 }
else if (transformType == QStringLiteral(
"rotate")) {
176 QSvgAnimatedPropertyTransform::TransformComponent component;
177 qreal rotationAngle = QSvgUtils::parseAngle(args.value(0)).value_or(0);
178 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Rotate;
179 component.values.append(rotationAngle);
180 component.values.append(0);
181 component.values.append(0);
182 components.append(component);
183 }
else if (transformType == QStringLiteral(
"skew")) {
184 QSvgAnimatedPropertyTransform::TransformComponent component;
185 qreal skew0 = QSvgUtils::parseAngle(args.value(0)).value_or(0);
186 qreal skew1 = QSvgUtils::parseAngle(args.value(1)).value_or(0);
187 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
188 component.values.append(skew0);
189 component.values.append(skew1);
190 components.append(component);
191 }
else if (transformType == QStringLiteral(
"matrix")) {
192 QSvgAnimatedPropertyTransform::TransformComponent component1, component2, component3;
193 QSvgUtils::LengthType type;
194 qreal translate0 = QSvgUtils::parseLength(args.value(4), &type);
195 translate0 = QSvgUtils::convertToPixels(translate0,
false, type);
196 qreal translate1 = QSvgUtils::parseLength(args.value(5), &type);
197 translate1 = QSvgUtils::convertToPixels(translate1,
false, type);
198 qreal scale0 = QSvgUtils::toDouble(args.value(0).trimmed());
199 qreal scale1 = QSvgUtils::toDouble(args.value(3).trimmed());
200 qreal skew0 = QSvgUtils::toDouble((args.value(1).trimmed()));
201 qreal skew1 = QSvgUtils::toDouble((args.value(2).trimmed()));
202 component1.type = QSvgAnimatedPropertyTransform::TransformComponent::Translate;
203 component1.values.append(translate0);
204 component1.values.append(translate1);
205 component2.type = QSvgAnimatedPropertyTransform::TransformComponent::Scale;
206 component2.values.append(scale0);
207 component2.values.append(scale1);
208 component3.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
209 component3.values.append(skew0);
210 component3.values.append(skew1);
211 components.append(component1);
212 components.append(component2);
213 components.append(component3);
217 keyFramesComponents.append(components);
218 prop->appendKeyFrame(keyFrame.keyFrame);
221 if (!validateTransform(keyFramesComponents))
224 for (
const auto &comp : std::as_const(keyFramesComponents)) {
225 prop->appendComponents(comp);
227 prop->setTransformCount(keyFramesComponents.first().size());
270QSvgCssAnimation *QSvgCssHandler::createAnimation(QStringView name)
272 if (!m_animations.contains(name))
275 QCss::AnimationRule animationRule = m_animations[name];
276 QHash<QString, QSvgAbstractAnimatedProperty*> animatedProperies;
277 QSvgCssAnimation *animation =
new QSvgCssAnimation;
282 QHash<QString, QList<CssKeyFrameValue>> keyFrameValues;
283 for (
const auto &ruleSet : std::as_const(animationRule.ruleSets)) {
284 for (
const QCss::Declaration &decl : ruleSet.declarations) {
285 QCss::Value timingFunction;
286 CssKeyFrameValue keyFrameValue = {ruleSet.keyFrame, decl.d->values, ruleSet.timingFunction};
287 QList<CssKeyFrameValue> &value = keyFrameValues[decl.d->property];
288 value.append(keyFrameValue);
292 for (
auto it = keyFrameValues.begin(); it != keyFrameValues.end(); it++) {
293 QStringView property = it.key();
294 const QList<CssKeyFrameValue> &keyFrames = it.value();
295 auto *prop = QSvgAbstractAnimatedProperty::createAnimatedProperty(property.toString());
300 if (property == QLatin1StringView(
"fill") || property == QLatin1StringView(
"stroke"))
301 result = fillColorProperty(keyFrames,
static_cast<QSvgAnimatedPropertyColor*>(prop));
302 else if (property == QLatin1StringView(
"transform"))
303 result = fillTransformProperty(keyFrames,
static_cast<QSvgAnimatedPropertyTransform*>(prop));
304 else if (property == QLatin1StringView(
"fill-opacity") || property == QLatin1StringView(
"stroke-opacity")
305 || property == QLatin1StringView(
"opacity"))
306 result = fillOpacityProperty(keyFrames,
static_cast<QSvgAnimatedPropertyFloat*>(prop));
307 else if (property == QLatin1StringView(
"offset-distance"))
308 result = fillOffsetDistanceProperty(keyFrames,
static_cast<QSvgAnimatedPropertyFloat*>(prop));
310 result &= fillPropertyEasing(keyFrames, prop);
316 animatedProperies[property] = prop;
319 for (
auto it = animatedProperies.begin(); it != animatedProperies.end(); it++)
320 animation->appendProperty(it.value());
372QString QSvgCssHandler::parseDecltoString(
const QCss::Declaration &decl)
374 if (decl.d->property.isEmpty())
378 const int valCount = decl.d->values.size();
379 for (
int i = 0; i < valCount; ++i) {
380 QCss::Value val = decl.d->values.at(i);
382 case QCss::Value::TermOperatorComma:
383 valueStr += QLatin1Char(
';');
385 case QCss::Value::Uri:
387 QString temp = val.toString();
388 temp.prepend(QLatin1String(
"url("));
389 temp.append(QLatin1Char(
')'));
393 case QCss::Value::Function:
395 QStringList lst = val.variant.toStringList();
396 valueStr.append(lst.at(0));
397 valueStr.append(QLatin1Char(
'('));
398 for (
int i = 1; i < lst.size(); ++i) {
399 valueStr.append(lst.at(i));
400 if ((i +1) < lst.size())
401 valueStr.append(QLatin1Char(
','));
403 valueStr.append(QLatin1Char(
')'));
406 case QCss::Value::KnownIdentifier:
407 switch (val.variant.toInt()) {
408 case QCss::Value_None:
409 valueStr += QLatin1String(
"none");
411 case QCss::Value_Auto:
412 valueStr += QLatin1String(
"auto");
415 valueStr += val.toString();
419 case QCss::Value::Percentage:
420 valueStr += val.toString() + QLatin1Char(
'%');
423 valueStr += val.toString();
427 if (i + 1 < valCount)
428 valueStr += QLatin1Char(
' ');
444void QSvgCssHandler::parseCSStoXMLAttrs(
const QString &css, QXmlStreamAttributes &attributes)
const
447 QCss::Parser parser(css);
450 while (parser.hasNext()) {
453 if (!parser.hasNext())
460 if (parser.hasEscapeSequences) {
461 key = parser.lexem();
464 const QCss::Symbol &sym = parser.symbol();
465 name = sym.text.mid(sym.start, sym.len);
469 if (!parser.test(QCss::COLON))
473 if (!parser.hasNext())
476 const int firstSymbol = parser.index;
481 }
while (parser.hasNext() && !parser.test(QCss::SEMICOLON));
483 bool canExtractValueByRef = !parser.hasEscapeSequences;
484 if (canExtractValueByRef) {
485 int len = parser.symbols.at(firstSymbol).len;
486 for (
int i = firstSymbol + 1; i < firstSymbol + symbolCount; ++i) {
487 len += parser.symbols.at(i).len;
489 if (parser.symbols.at(i - 1).start + parser.symbols.at(i - 1).len
490 != parser.symbols.at(i).start) {
491 canExtractValueByRef =
false;
495 if (canExtractValueByRef) {
496 const QCss::Symbol &sym = parser.symbols.at(firstSymbol);
497 value = sym.text.mid(sym.start, len);
500 if (!canExtractValueByRef) {
502 for (
int i = firstSymbol; i < parser.index - 1; ++i)
503 value += parser.symbols.at(i).lexem();
506 attributes.append(QString(), name, value);