Revision control
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include "qsvgvisitor_p.h"
+ #include <QStack>
+ #include <utility>
QT_BEGIN_NAMESPACE
+ namespace {
+
+ bool isStructure(const QSvgNode *node)
+ {
+ switch (node->type()) {
+ case QSvgNode::Switch:
+ case QSvgNode::Doc:
+ case QSvgNode::Defs:
+ case QSvgNode::Group:
+ case QSvgNode::Mask:
+ case QSvgNode::Symbol:
+ case QSvgNode::Filter:
+ case QSvgNode::FeMerge:
+ case QSvgNode::FeMergenode:
+ case QSvgNode::FeColormatrix:
+ case QSvgNode::FeGaussianblur:
+ case QSvgNode::FeOffset:
+ case QSvgNode::FeComposite:
+ case QSvgNode::FeFlood:
+ case QSvgNode::FeBlend:
+ case QSvgNode::FeUnsupported:
+ case QSvgNode::Marker:
+ case QSvgNode::Pattern:
+ return true;
+ case QSvgNode::AnimateColor:
+ case QSvgNode::AnimateTransform:
+ case QSvgNode::Circle:
+ case QSvgNode::Ellipse:
+ case QSvgNode::Image:
+ case QSvgNode::Line:
+ case QSvgNode::Path:
+ case QSvgNode::Polygon:
+ case QSvgNode::Polyline:
+ case QSvgNode::Rect:
+ case QSvgNode::Text:
+ case QSvgNode::Textarea:
+ case QSvgNode::Tspan:
+ case QSvgNode::Use:
+ case QSvgNode::Video:
+ case QSvgNode::Font:
+ return false;
+ }
+
+ Q_UNREACHABLE_RETURN(false);
+ }
+
+ } // namespace
+
QSvgVisitor::~QSvgVisitor()
= default;
- void QSvgVisitor::traverse(const QSvgStructureNode *node)
+ void QSvgVisitor::traverse(const QSvgNode *node)
+ {
+ // A pair that keeps track of a node and a visited flag.
+ using NodeState = std::pair<const QSvgNode *, bool>;
+ QStack<NodeState> nodes;
+ nodes.push({node, false});
+
+ do {
+ NodeState state = nodes.pop();
+ const QSvgNode *current = state.first;
+ const bool visited = state.second;
+ if (isStructure(current)) {
+ const QSvgStructureNode *structure = static_cast<const QSvgStructureNode *>(current);
+ if (!visited) {
+ if (!traverseStructureNodeStart(structure))
+ continue;
+ nodes.push({structure, true});
+ for (auto it = structure->renderers().crbegin(); it != structure->renderers().crend(); it++)
+ nodes.push({it->get(), false});
+ } else {
+ traverseStructureNodeEnd(structure);
+ }
+ } else {
+ traverseLeafNode(current);
+ }
+
+ } while (!nodes.isEmpty());
+ }
+
+ bool QSvgVisitor::traverseStructureNodeStart(const QSvgStructureNode *node)
{
switch (node->type()) {
case QSvgNode::Switch:
- if (!visitSwitchNodeStart(static_cast<const QSvgSwitch *>(node)))
- return;
- break;
+ return visitSwitchNodeStart(static_cast<const QSvgSwitch *>(node));
case QSvgNode::Doc:
- if (!visitDocumentNodeStart(static_cast<const QSvgDocument *>(node)))
- return;
- break;
+ return visitDocumentNodeStart(static_cast<const QSvgDocument *>(node));
case QSvgNode::Defs:
- if (!visitDefsNodeStart(static_cast<const QSvgDefs *>(node)))
- return;
- break;
+ return visitDefsNodeStart(static_cast<const QSvgDefs *>(node));
case QSvgNode::Group:
- if (!visitGroupNodeStart(static_cast<const QSvgG *>(node)))
- return;
- break;
+ return visitGroupNodeStart(static_cast<const QSvgG *>(node));
case QSvgNode::Mask:
- if (!visitMaskNodeStart(static_cast<const QSvgMask *>(node)))
- return;
- break;
+ return visitMaskNodeStart(static_cast<const QSvgMask *>(node));
case QSvgNode::Symbol:
- if (!visitSymbolNodeStart(static_cast<const QSvgSymbol *>(node)))
- return;
- break;
+ return visitSymbolNodeStart(static_cast<const QSvgSymbol *>(node));
case QSvgNode::Filter:
- if (!visitFilterNodeStart(static_cast<const QSvgFilterContainer *>(node)))
- return;
- break;
+ return visitFilterNodeStart(static_cast<const QSvgFilterContainer *>(node));
case QSvgNode::Marker:
- if (!visitMarkerNodeStart(static_cast<const QSvgMarker *>(node)))
- return;
- break;
+ return visitMarkerNodeStart(static_cast<const QSvgMarker *>(node));
case QSvgNode::Pattern:
- if (!visitPatternNodeStart(static_cast<const QSvgPattern *>(node)))
- return;
- break;
+ return visitPatternNodeStart(static_cast<const QSvgPattern *>(node));
case QSvgNode::FeMerge:
case QSvgNode::FeMergenode:
case QSvgNode::FeColormatrix:
case QSvgNode::FeGaussianblur:
case QSvgNode::FeOffset:
case QSvgNode::FeComposite:
case QSvgNode::FeFlood:
case QSvgNode::FeBlend:
- if (!visitFeFilterPrimitiveNodeStart(static_cast<const QSvgFeFilterPrimitive *>(node)))
- return;
+ return visitFeFilterPrimitiveNodeStart(static_cast<const QSvgFeFilterPrimitive *>(node));
+ // Enum values that are either not supported or should not be visited:
+ case QSvgNode::FeUnsupported:
+ qDebug() << "Unhandled type in switch" << node->type();
break;
default:
Q_UNREACHABLE();
break;
}
- for (auto &child : node->renderers())
- traverse(child.get());
+ return true;
+ }
+ void QSvgVisitor::traverseStructureNodeEnd(const QSvgStructureNode *node)
+ {
switch (node->type()) {
case QSvgNode::Switch:
visitSwitchNodeEnd(static_cast<const QSvgSwitch *>(node));
break;
case QSvgNode::Doc:
visitDocumentNodeEnd(static_cast<const QSvgDocument *>(node));
break;
case QSvgNode::Defs:
visitDefsNodeEnd(static_cast<const QSvgDefs *>(node));
break;
case QSvgNode::Group:
visitGroupNodeEnd(static_cast<const QSvgG *>(node));
break;
case QSvgNode::Mask:
visitMaskNodeEnd(static_cast<const QSvgMask *>(node));
break;
case QSvgNode::Symbol:
visitSymbolNodeEnd(static_cast<const QSvgSymbol *>(node));
break;
case QSvgNode::Filter:
visitFilterNodeEnd(static_cast<const QSvgFilterContainer *>(node));
break;
case QSvgNode::Marker:
visitMarkerNodeEnd(static_cast<const QSvgMarker *>(node));
break;
case QSvgNode::Pattern:
visitPatternNodeEnd(static_cast<const QSvgPattern *>(node));
break;
case QSvgNode::FeMerge:
case QSvgNode::FeMergenode:
case QSvgNode::FeColormatrix:
case QSvgNode::FeGaussianblur:
case QSvgNode::FeOffset:
case QSvgNode::FeComposite:
case QSvgNode::FeFlood:
case QSvgNode::FeBlend:
visitFeFilterPrimitiveNodeEnd(static_cast<const QSvgFeFilterPrimitive *>(node));
break;
+ // Enum values that are either not supported or should not be visited:
+ case QSvgNode::FeUnsupported:
+ qDebug() << "Unhandled type in switch" << node->type();
+ break;
default:
Q_UNREACHABLE();
break;
}
}
- void QSvgVisitor::traverse(const QSvgNode *node)
+ void QSvgVisitor::traverseLeafNode(const QSvgNode *node)
{
switch (node->type()) {
- case QSvgNode::Switch:
- case QSvgNode::Doc:
- case QSvgNode::Defs:
- case QSvgNode::Group:
- case QSvgNode::Mask:
- case QSvgNode::Symbol:
- case QSvgNode::Filter:
- case QSvgNode::FeMerge:
- case QSvgNode::FeMergenode:
- case QSvgNode::FeColormatrix:
- case QSvgNode::FeGaussianblur:
- case QSvgNode::FeOffset:
- case QSvgNode::FeComposite:
- case QSvgNode::FeFlood:
- case QSvgNode::FeBlend:
- case QSvgNode::Marker:
- case QSvgNode::Pattern:
- traverse(static_cast<const QSvgStructureNode *>(node));
- break;
case QSvgNode::AnimateColor:
case QSvgNode::AnimateTransform:
visitAnimateNode(static_cast<const QSvgAnimateNode *>(node));
break;
case QSvgNode::Circle:
case QSvgNode::Ellipse:
visitEllipseNode(static_cast<const QSvgEllipse *>(node));
break;
case QSvgNode::Image:
visitImageNode(static_cast<const QSvgImage *>(node));
break;
case QSvgNode::Line:
visitLineNode(static_cast<const QSvgLine *>(node));
break;
case QSvgNode::Path:
visitPathNode(static_cast<const QSvgPath *>(node));
break;
case QSvgNode::Polygon:
visitPolygonNode(static_cast<const QSvgPolygon *>(node));
break;
case QSvgNode::Polyline:
visitPolylineNode(static_cast<const QSvgPolyline *>(node));
break;
case QSvgNode::Rect:
visitRectNode(static_cast<const QSvgRect *>(node));
break;
case QSvgNode::Text:
case QSvgNode::Textarea:
visitTextNode(static_cast<const QSvgText *>(node));
break;
case QSvgNode::Tspan:
visitTspanNode(static_cast<const QSvgTspan *>(node));
break;
case QSvgNode::Use:
visitUseNode(static_cast<const QSvgUse *>(node));
break;
case QSvgNode::Video:
visitVideoNode(static_cast<const QSvgVideo *>(node));
break;
-
- // Enum values that are either not supported or should not be visited:
- case QSvgNode::FeUnsupported:
+ // Enum values that are either not supported or should not be visited:
case QSvgNode::Font:
qDebug() << "Unhandled type in switch" << node->type();
break;
+ default:
+ Q_UNREACHABLE();
+ break;
}
}
QT_END_NAMESPACE