summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Olav Tvete <paul.tvete@qt.io>2023-10-05 08:43:55 +0200
committerPaul Olav Tvete <paul.tvete@qt.io>2023-10-30 10:36:06 +0200
commite9913b70717f55efb88e3022254299c848bcb940 (patch)
tree99672b7cfe9d10a5ae84f8610cb9cab546db747f /src
parent24e0d715a6e14f06c9da7604086789431960a41a (diff)
Private API for iterating over QSvgTinyDocument
Expose more node information in the internal API. Also use the new API to implement a debug operator for QSvgTinyDocument. Includes a drive-by fix: use floating point instead of int for rounded rect radius. Task-number: QTBUG-116883 Change-Id: I238b7313c10b88abd7c2ca755b898ecb6f8c3e8b Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/svg/CMakeLists.txt2
-rw-r--r--src/svg/qsvgdebug.cpp185
-rw-r--r--src/svg/qsvggraphics.cpp2
-rw-r--r--src/svg/qsvggraphics_p.h26
-rw-r--r--src/svg/qsvghandler.cpp4
-rw-r--r--src/svg/qsvgnode_p.h1
-rw-r--r--src/svg/qsvgstyle_p.h7
-rw-r--r--src/svg/qsvgtinydocument_p.h2
-rw-r--r--src/svg/qsvgvisitor.cpp119
-rw-r--r--src/svg/qsvgvisitor_p.h57
10 files changed, 397 insertions, 8 deletions
diff --git a/src/svg/CMakeLists.txt b/src/svg/CMakeLists.txt
index bb01c53..0bea57c 100644
--- a/src/svg/CMakeLists.txt
+++ b/src/svg/CMakeLists.txt
@@ -23,6 +23,8 @@ qt_internal_add_module(Svg
qsvgstructure.cpp qsvgstructure_p.h
qsvgstyle.cpp qsvgstyle_p.h
qsvgtinydocument.cpp qsvgtinydocument_p.h
+ qsvgdebug.cpp
+ qsvgvisitor.cpp qsvgvisitor_p.h
qtsvgglobal.h qtsvgglobal_p.h
DEFINES
QT_NO_CONTEXTLESS_CONNECT
diff --git a/src/svg/qsvgdebug.cpp b/src/svg/qsvgdebug.cpp
new file mode 100644
index 0000000..a3cdf8d
--- /dev/null
+++ b/src/svg/qsvgdebug.cpp
@@ -0,0 +1,185 @@
+// 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
+
+#include "qsvgvisitor_p.h"
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+static const char *nodeTypeStrings[] = {
+ "DOC",
+ "G",
+ "DEFS",
+ "SWITCH",
+ "ANIMATION",
+ "ARC",
+ "CIRCLE",
+ "ELLIPSE",
+ "IMAGE",
+ "LINE",
+ "PATH",
+ "POLYGON",
+ "POLYLINE",
+ "RECT",
+ "TEXT",
+ "TEXTAREA",
+ "TSPAN",
+ "USE",
+ "VIDEO"
+};
+
+// TODO: something like this is needed in several places. Make a common version.
+static const char *typeName(const QSvgNode *node)
+{
+ constexpr int typeNameCount = sizeof(nodeTypeStrings) / sizeof(const char *);
+ if (node->type() < typeNameCount)
+ return nodeTypeStrings[node->type()];
+ return "UNKNOWN";
+}
+
+class SvgDebugVisitor : public QSvgVisitor
+{
+public:
+ SvgDebugVisitor(QDebug &stream) : debug(stream) {}
+ void write(const QSvgTinyDocument *doc);
+
+protected:
+ void visitNode(const QSvgNode *) override;
+ bool visitStructureNodeStart(const QSvgStructureNode *node) override;
+ void visitStructureNodeEnd(const QSvgStructureNode *) override;
+ void visitAnimationNode(const QSvgAnimation *node) override;
+ void visitEllipseNode(const QSvgEllipse *node) override;
+ void visitImageNode(const QSvgImage *node) override;
+ void visitLineNode(const QSvgLine *node) override;
+ void visitPathNode(const QSvgPath *node) override;
+ void visitPolygonNode(const QSvgPolygon *node) override;
+ void visitPolylineNode(const QSvgPolyline *node) override;
+ void visitRectNode(const QSvgRect *node) override;
+ void visitTextNode(const QSvgText *node) override;
+ void visitUseNode(const QSvgUse *node) override;
+ void visitVideoNode(const QSvgVideo *node) override;
+
+private:
+ const char *indent() { m_indent.fill(' ', m_indentLevel * 2); return m_indent.constData();}
+ void handleBaseNode(const QSvgNode *node);
+ QDebug &debug;
+ int m_indentLevel = 0;
+ QByteArray m_indent;
+ int nodeCounter = 0;
+};
+
+void SvgDebugVisitor::handleBaseNode(const QSvgNode *node)
+{
+ debug << indent() << typeName(node) << "node, ID:" << node->nodeId();
+ nodeCounter++;
+}
+
+void SvgDebugVisitor::visitNode(const QSvgNode *node)
+{
+ handleBaseNode(node);
+ debug << Qt::endl;
+}
+
+bool SvgDebugVisitor::visitStructureNodeStart(const QSvgStructureNode *node)
+{
+ debug << indent() << "START node" << node->nodeId() << "type" << typeName(node) << node->type() << Qt::endl;
+ m_indentLevel++;
+ return true;
+}
+
+void SvgDebugVisitor::visitStructureNodeEnd(const QSvgStructureNode *node)
+{
+ m_indentLevel--;
+ debug << indent() << "END node" << node->nodeId() << Qt::endl;
+}
+
+void SvgDebugVisitor::visitAnimationNode(const QSvgAnimation *node)
+{
+ handleBaseNode(node);
+ debug << Qt::endl;
+}
+
+void SvgDebugVisitor::visitEllipseNode(const QSvgEllipse *node)
+{
+ handleBaseNode(node);
+ debug << "rect:" << node->rect() << Qt::endl;
+}
+
+void SvgDebugVisitor::visitImageNode(const QSvgImage *node)
+{
+ handleBaseNode(node);
+ debug << "image:" << node->image() << Qt::endl;
+}
+
+void SvgDebugVisitor::visitLineNode(const QSvgLine *node)
+{
+ handleBaseNode(node);
+ debug << "line:" << node->line() << Qt::endl;
+}
+
+void SvgDebugVisitor::visitPathNode(const QSvgPath *node)
+{
+ handleBaseNode(node);
+ debug << "path:" << node->path().elementCount() << "elements." << Qt::endl;
+}
+
+void SvgDebugVisitor::visitPolygonNode(const QSvgPolygon *node)
+{
+ handleBaseNode(node);
+ debug << "polygon:" << node->polygon().size() << "elements." << Qt::endl;
+}
+
+void SvgDebugVisitor::visitPolylineNode(const QSvgPolyline *node)
+{
+ handleBaseNode(node);
+ debug << "polygon:" << node->polygon().size() << "elements." << Qt::endl;
+}
+
+void SvgDebugVisitor::visitRectNode(const QSvgRect *node)
+{
+ handleBaseNode(node);
+ debug << "rect:" << node->rect() << "radius:" << node->radius() << Qt::endl;
+}
+
+void SvgDebugVisitor::visitTextNode(const QSvgText *node)
+{
+ handleBaseNode(node);
+ QString text;
+ for (const auto *tspan : node->tspans()) {
+ if (!tspan)
+ text += QStringLiteral("\\n");
+ else
+ text += tspan->text();
+ }
+ debug << "text:" << text << Qt::endl;
+}
+
+void SvgDebugVisitor::visitUseNode(const QSvgUse *node)
+{
+ handleBaseNode(node);
+ debug << "link ID:" << node->linkId() << Qt::endl;
+}
+
+void SvgDebugVisitor::visitVideoNode(const QSvgVideo *node)
+{
+ handleBaseNode(node);
+ debug << Qt::endl;
+}
+
+void SvgDebugVisitor::write(const QSvgTinyDocument *doc)
+{
+ debug << "SVG" << doc->size() << "viewBox" << doc->viewBox() << Qt::endl;
+ traverse(doc);
+
+ debug << "END SVG" << nodeCounter << "nodes";
+}
+
+QDebug operator<<(QDebug debug, const QSvgTinyDocument &doc)
+{
+ SvgDebugVisitor visitor(debug);
+ visitor.write(&doc);
+
+ return debug;
+}
+
+QT_END_NAMESPACE
diff --git a/src/svg/qsvggraphics.cpp b/src/svg/qsvggraphics.cpp
index 5cb216b..1858d8e 100644
--- a/src/svg/qsvggraphics.cpp
+++ b/src/svg/qsvggraphics.cpp
@@ -250,7 +250,7 @@ void QSvgPolyline::draw(QPainter *p, QSvgExtraStates &states)
QT_SVG_TIMING_EXIT("Polyline")
}
-QSvgRect::QSvgRect(QSvgNode *node, const QRectF &rect, int rx, int ry)
+QSvgRect::QSvgRect(QSvgNode *node, const QRectF &rect, qreal rx, qreal ry)
: QSvgNode(node),
m_rect(rect), m_rx(rx), m_ry(ry)
{
diff --git a/src/svg/qsvggraphics_p.h b/src/svg/qsvggraphics_p.h
index 9bb005b..42406e3 100644
--- a/src/svg/qsvggraphics_p.h
+++ b/src/svg/qsvggraphics_p.h
@@ -55,6 +55,7 @@ public:
Type type() const override;
QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override;
QRectF bounds(QPainter *p, QSvgExtraStates &states) const override;
+ QRectF rect() const { return m_bounds; }
private:
QRectF m_bounds;
};
@@ -74,6 +75,9 @@ public:
void draw(QPainter *p, QSvgExtraStates &states) override;
Type type() const override;
QRectF bounds(QPainter *p, QSvgExtraStates &states) const override;
+
+ QRectF rect() const { return m_bounds; }
+ const QImage &image() const { return m_image; }
private:
QImage m_image;
QRectF m_bounds;
@@ -87,6 +91,7 @@ public:
Type type() const override;
QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override;
QRectF bounds(QPainter *p, QSvgExtraStates &states) const override;
+ QLineF line() const { return m_line; }
private:
QLineF m_line;
};
@@ -100,9 +105,10 @@ public:
QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override;
QRectF bounds(QPainter *p, QSvgExtraStates &states) const override;
- QPainterPath *qpath() {
- return &m_path;
+ const QPainterPath &path() const {
+ return m_path;
}
+
private:
QPainterPath m_path;
};
@@ -115,6 +121,9 @@ public:
Type type() const override;
QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override;
QRectF bounds(QPainter *p, QSvgExtraStates &states) const override;
+ const QPolygonF &polygon() const {
+ return m_poly;
+ }
private:
QPolygonF m_poly;
};
@@ -127,6 +136,9 @@ public:
Type type() const override;
QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override;
QRectF bounds(QPainter *p, QSvgExtraStates &states) const override;
+ const QPolygonF &polygon() const {
+ return m_poly;
+ }
private:
QPolygonF m_poly;
};
@@ -134,14 +146,16 @@ private:
class Q_SVG_PRIVATE_EXPORT QSvgRect : public QSvgNode
{
public:
- QSvgRect(QSvgNode *paren, const QRectF &rect, int rx=0, int ry=0);
+ QSvgRect(QSvgNode *paren, const QRectF &rect, qreal rx=0, qreal ry=0);
Type type() const override;
void draw(QPainter *p, QSvgExtraStates &states) override;
QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override;
QRectF bounds(QPainter *p, QSvgExtraStates &states) const override;
+ QRectF rect() const { return m_rect; }
+ QPointF radius() const { return { m_rx, m_ry }; }
private:
QRectF m_rect;
- int m_rx, m_ry;
+ qreal m_rx, m_ry;
};
class QSvgTspan;
@@ -163,6 +177,7 @@ public:
Type type() const override;
void addTspan(QSvgTspan *tspan) {m_tspans.append(tspan);}
+ const QList<QSvgTspan *> tspans() const { return m_tspans; }
void addText(const QString &text);
void addLineBreak() {m_tspans.append(LINEBREAK);}
void setWhitespaceMode(WhitespaceMode mode) {m_mode = mode;}
@@ -170,6 +185,9 @@ public:
QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override;
QRectF bounds(QPainter *p, QSvgExtraStates &states) const override;
+ QPointF position() const { return m_coord; }
+ QSizeF size() const { return m_size; }
+
private:
bool precheck(QPainter *p) const;
void draw_helper(QPainter *p, QSvgExtraStates &states, QRectF *boundingRect = nullptr) const;
diff --git a/src/svg/qsvghandler.cpp b/src/svg/qsvghandler.cpp
index c870081..08f6e71 100644
--- a/src/svg/qsvghandler.cpp
+++ b/src/svg/qsvghandler.cpp
@@ -3089,9 +3089,7 @@ static QSvgNode *createRectNode(QSvgNode *parent,
nrx *= (100/(bounds.width()/2));
nry *= (100/(bounds.height()/2));
- QSvgNode *rect = new QSvgRect(parent, bounds,
- int(nrx),
- int(nry));
+ QSvgNode *rect = new QSvgRect(parent, bounds, nrx, nry);
return rect;
}
diff --git a/src/svg/qsvgnode_p.h b/src/svg/qsvgnode_p.h
index df1341b..2c23b2f 100644
--- a/src/svg/qsvgnode_p.h
+++ b/src/svg/qsvgnode_p.h
@@ -133,6 +133,7 @@ public:
void setXmlClass(const QString &str);
bool shouldDrawNode(QPainter *p, QSvgExtraStates &states) const;
+ const QSvgStyle &style() const { return m_style; }
protected:
mutable QSvgStyle m_style;
diff --git a/src/svg/qsvgstyle_p.h b/src/svg/qsvgstyle_p.h
index 20bd6d5..9581524 100644
--- a/src/svg/qsvgstyle_p.h
+++ b/src/svg/qsvgstyle_p.h
@@ -74,6 +74,8 @@ public:
inline T *operator->() const { return t; }
inline operator T*() const { return t; }
+ inline bool isDefault() const { return !t || t->isDefault(); }
+
private:
T *t;
};
@@ -138,6 +140,7 @@ public:
virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states) = 0;
virtual void revert(QPainter *p, QSvgExtraStates &states) =0;
virtual Type type() const=0;
+ bool isDefault() const { return false; } // [not virtual since called from templated class]
};
class Q_SVG_PRIVATE_EXPORT QSvgFillStyleProperty : public QSvgStyleProperty
@@ -197,6 +200,9 @@ public:
void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states) override;
void revert(QPainter *p, QSvgExtraStates &states) override;
Type type() const override;
+ qreal opacity() const { return m_opacity; }
+ bool isDefault() const { return qFuzzyCompare(m_opacity, 1.0); }
+
private:
qreal m_opacity;
qreal m_oldOpacity;
@@ -597,6 +603,7 @@ public:
{
return m_transform;
}
+ bool isDefault() const { return m_transform.isIdentity(); }
private:
//7.6 The transform attribute
QTransform m_transform;
diff --git a/src/svg/qsvgtinydocument_p.h b/src/svg/qsvgtinydocument_p.h
index 7f0a9af..539db9c 100644
--- a/src/svg/qsvgtinydocument_p.h
+++ b/src/svg/qsvgtinydocument_p.h
@@ -111,6 +111,8 @@ private:
const QSvg::FeatureSet m_featureSet;
};
+Q_SVG_PRIVATE_EXPORT QDebug operator<<(QDebug debug, const QSvgTinyDocument &doc);
+
inline QSize QSvgTinyDocument::size() const
{
if (m_size.isEmpty())
diff --git a/src/svg/qsvgvisitor.cpp b/src/svg/qsvgvisitor.cpp
new file mode 100644
index 0000000..fe3ea72
--- /dev/null
+++ b/src/svg/qsvgvisitor.cpp
@@ -0,0 +1,119 @@
+// 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
+
+#include "qsvgvisitor_p.h"
+
+QT_BEGIN_NAMESPACE
+
+void QSvgVisitor::traverse(const QSvgStructureNode *node)
+{
+ switch (node->type()) {
+ case QSvgNode::SWITCH:
+ if (!visitSwitchNodeStart(static_cast<const QSvgSwitch *>(node)))
+ return;
+ break;
+ case QSvgNode::DOC:
+ if (!visitDocumentNodeStart(static_cast<const QSvgTinyDocument *>(node)))
+ return;
+ break;
+ case QSvgNode::DEFS:
+ if (!visitDefsNodeStart(static_cast<const QSvgDefs *>(node)))
+ return;
+ break;
+ case QSvgNode::G:
+ if (!visitGroupNodeStart(static_cast<const QSvgG *>(node)))
+ return;
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ for (const auto *child : node->renderers()) {
+ switch (child->type()) {
+ case QSvgNode::SWITCH:
+ case QSvgNode::DOC:
+ case QSvgNode::DEFS:
+ case QSvgNode::G:
+ traverse(static_cast<const QSvgStructureNode *>(child));
+ break;
+ case QSvgNode::ANIMATION:
+ visitAnimationNode(static_cast<const QSvgAnimation *>(child));
+ break;
+ case QSvgNode::CIRCLE:
+ case QSvgNode::ELLIPSE:
+ visitEllipseNode(static_cast<const QSvgEllipse *>(child));
+ break;
+ case QSvgNode::IMAGE:
+ visitImageNode(static_cast<const QSvgImage *>(child));
+ break;
+ case QSvgNode::LINE:
+ visitLineNode(static_cast<const QSvgLine *>(child));
+ break;
+ case QSvgNode::PATH:
+ visitPathNode(static_cast<const QSvgPath *>(child));
+ break;
+ case QSvgNode::POLYGON:
+ visitPolygonNode(static_cast<const QSvgPolygon *>(child));
+ break;
+ case QSvgNode::POLYLINE:
+ visitPolylineNode(static_cast<const QSvgPolyline *>(child));
+ break;
+ case QSvgNode::RECT:
+ visitRectNode(static_cast<const QSvgRect *>(child));
+ break;
+ case QSvgNode::TEXT:
+ case QSvgNode::TEXTAREA:
+ visitTextNode(static_cast<const QSvgText *>(child));
+ break;
+ case QSvgNode::TSPAN:
+ visitTspanNode(static_cast<const QSvgTspan *>(child));
+ break;
+ case QSvgNode::USE:
+ visitUseNode(static_cast<const QSvgUse *>(child));
+ break;
+ case QSvgNode::VIDEO:
+ visitVideoNode(static_cast<const QSvgVideo *>(child));
+ break;
+
+ // Enum values that don't have any QSvgNode classes yet:
+ case QSvgNode::MASK:
+ case QSvgNode::SYMBOL:
+ case QSvgNode::MARKER:
+ case QSvgNode::PATTERN:
+ case QSvgNode::FILTER:
+ case QSvgNode::FEMERGE:
+ case QSvgNode::FEMERGENODE:
+ case QSvgNode::FECOLORMATRIX:
+ case QSvgNode::FEGAUSSIANBLUR:
+ case QSvgNode::FEOFFSET:
+ case QSvgNode::FECOMPOSITE:
+ case QSvgNode::FEFLOOD:
+ qDebug() << "Unhandled type in switch" << child->type();
+ break;
+
+ case QSvgNode::ARC: // Not used: to be removed
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+ switch (node->type()) {
+ case QSvgNode::SWITCH:
+ visitSwitchNodeEnd(static_cast<const QSvgSwitch *>(node));
+ break;
+ case QSvgNode::DOC:
+ visitDocumentNodeEnd(static_cast<const QSvgTinyDocument *>(node));
+ break;
+ case QSvgNode::DEFS:
+ visitDefsNodeEnd(static_cast<const QSvgDefs *>(node));
+ break;
+ case QSvgNode::G:
+ visitGroupNodeEnd(static_cast<const QSvgG *>(node));
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/svg/qsvgvisitor_p.h b/src/svg/qsvgvisitor_p.h
new file mode 100644
index 0000000..dccff1a
--- /dev/null
+++ b/src/svg/qsvgvisitor_p.h
@@ -0,0 +1,57 @@
+// 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
+
+#ifndef QSVGVISITOR_P_H
+#define QSVGVISITOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsvgtinydocument_p.h>
+#include <private/qsvghandler_p.h>
+#include <private/qsvggraphics_p.h>
+#include <private/qsvgstructure_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_SVG_PRIVATE_EXPORT QSvgVisitor {
+public:
+ void traverse(const QSvgStructureNode *node);
+protected:
+ virtual void visitNode(const QSvgNode *) {}
+ virtual bool visitStructureNodeStart(const QSvgStructureNode *node) { visitNode(node); return true; }
+ virtual void visitStructureNodeEnd(const QSvgStructureNode *) {}
+ virtual void visitAnimationNode(const QSvgAnimation *node) { visitNode(node); }
+ virtual void visitEllipseNode(const QSvgEllipse *node) { visitNode(node); }
+ virtual void visitImageNode(const QSvgImage *node) { visitNode(node); }
+ virtual void visitLineNode(const QSvgLine *node) { visitNode(node); }
+ virtual void visitPathNode(const QSvgPath *node) { visitNode(node); }
+ virtual void visitPolygonNode(const QSvgPolygon *node) { visitNode(node); }
+ virtual void visitPolylineNode(const QSvgPolyline *node) { visitNode(node); }
+ virtual void visitRectNode(const QSvgRect *node) { visitNode(node); }
+ virtual void visitTextNode(const QSvgText *node) { visitNode(node); }
+ virtual void visitTspanNode(const QSvgTspan *node) { visitNode(node); }
+ virtual void visitUseNode(const QSvgUse *node) { visitNode(node); }
+ virtual void visitVideoNode(const QSvgVideo *node) { visitNode(node); }
+
+ virtual bool visitDocumentNodeStart(const QSvgTinyDocument *node) { return visitStructureNodeStart(node); }
+ virtual void visitDocumentNodeEnd(const QSvgTinyDocument *node) { visitStructureNodeEnd(node); }
+ virtual bool visitGroupNodeStart(const QSvgG *node) { return visitStructureNodeStart(node); }
+ virtual void visitGroupNodeEnd(const QSvgG *node) { visitStructureNodeEnd(node); }
+ virtual bool visitDefsNodeStart(const QSvgDefs *node) { return visitStructureNodeStart(node); }
+ virtual void visitDefsNodeEnd(const QSvgDefs *node) { visitStructureNodeEnd(node); };
+ virtual bool visitSwitchNodeStart(const QSvgSwitch *node) { return visitStructureNodeStart(node); }
+ virtual void visitSwitchNodeEnd(const QSvgSwitch *node) { visitStructureNodeEnd(node); };
+};
+
+QT_END_NAMESPACE
+
+#endif // QSVGVISITOR_P_H