1// Copyright (C) 2016 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
4#include "qsvgnode_p.h"
5#include "qsvgtinydocument_p.h"
6
7#include <QLoggingCategory>
8
9#include "qdebug.h"
10#include "qstack.h"
11
12#include <QtGui/private/qoutlinemapper_p.h>
13
14#if !defined(QT_SVG_SIZE_LIMIT)
15# define QT_SVG_SIZE_LIMIT QT_RASTER_COORD_LIMIT
16#endif
17
18QT_BEGIN_NAMESPACE
19
20Q_DECLARE_LOGGING_CATEGORY(lcSvgDraw)
21
22QSvgNode::QSvgNode(QSvgNode *parent)
23 : m_parent(parent),
24 m_visible(true),
25 m_displayMode(BlockMode)
26{
27}
28
29QSvgNode::~QSvgNode()
30{
31
32}
33
34bool QSvgNode::isDescendantOf(const QSvgNode *parent) const
35{
36 const QSvgNode *n = this;
37 while (n) {
38 if (n == parent)
39 return true;
40 n = n->m_parent;
41 }
42 return false;
43}
44
45void QSvgNode::appendStyleProperty(QSvgStyleProperty *prop, const QString &id)
46{
47 //qDebug()<<"appending "<<prop->type()<< " ("<< id <<") "<<"to "<<this<<this->type();
48 QSvgTinyDocument *doc;
49 switch (prop->type()) {
50 case QSvgStyleProperty::QUALITY:
51 m_style.quality = static_cast<QSvgQualityStyle*>(prop);
52 break;
53 case QSvgStyleProperty::FILL:
54 m_style.fill = static_cast<QSvgFillStyle*>(prop);
55 break;
56 case QSvgStyleProperty::VIEWPORT_FILL:
57 m_style.viewportFill = static_cast<QSvgViewportFillStyle*>(prop);
58 break;
59 case QSvgStyleProperty::FONT:
60 m_style.font = static_cast<QSvgFontStyle*>(prop);
61 break;
62 case QSvgStyleProperty::STROKE:
63 m_style.stroke = static_cast<QSvgStrokeStyle*>(prop);
64 break;
65 case QSvgStyleProperty::SOLID_COLOR:
66 m_style.solidColor = static_cast<QSvgSolidColorStyle*>(prop);
67 doc = document();
68 if (doc && !id.isEmpty())
69 doc->addNamedStyle(id, style: m_style.solidColor);
70 break;
71 case QSvgStyleProperty::GRADIENT:
72 m_style.gradient = static_cast<QSvgGradientStyle*>(prop);
73 doc = document();
74 if (doc && !id.isEmpty())
75 doc->addNamedStyle(id, style: m_style.gradient);
76 break;
77 case QSvgStyleProperty::TRANSFORM:
78 m_style.transform = static_cast<QSvgTransformStyle*>(prop);
79 break;
80 case QSvgStyleProperty::ANIMATE_COLOR:
81 m_style.animateColor = static_cast<QSvgAnimateColor*>(prop);
82 break;
83 case QSvgStyleProperty::ANIMATE_TRANSFORM:
84 m_style.animateTransforms.append(
85 t: static_cast<QSvgAnimateTransform*>(prop));
86 break;
87 case QSvgStyleProperty::OPACITY:
88 m_style.opacity = static_cast<QSvgOpacityStyle*>(prop);
89 break;
90 case QSvgStyleProperty::COMP_OP:
91 m_style.compop = static_cast<QSvgCompOpStyle*>(prop);
92 break;
93 default:
94 qDebug(msg: "QSvgNode: Trying to append unknown property!");
95 break;
96 }
97}
98
99void QSvgNode::applyStyle(QPainter *p, QSvgExtraStates &states) const
100{
101 m_style.apply(p, node: this, states);
102}
103
104void QSvgNode::revertStyle(QPainter *p, QSvgExtraStates &states) const
105{
106 m_style.revert(p, states);
107}
108
109QSvgStyleProperty * QSvgNode::styleProperty(QSvgStyleProperty::Type type) const
110{
111 const QSvgNode *node = this;
112 while (node) {
113 switch (type) {
114 case QSvgStyleProperty::QUALITY:
115 if (node->m_style.quality)
116 return node->m_style.quality;
117 break;
118 case QSvgStyleProperty::FILL:
119 if (node->m_style.fill)
120 return node->m_style.fill;
121 break;
122 case QSvgStyleProperty::VIEWPORT_FILL:
123 if (m_style.viewportFill)
124 return node->m_style.viewportFill;
125 break;
126 case QSvgStyleProperty::FONT:
127 if (node->m_style.font)
128 return node->m_style.font;
129 break;
130 case QSvgStyleProperty::STROKE:
131 if (node->m_style.stroke)
132 return node->m_style.stroke;
133 break;
134 case QSvgStyleProperty::SOLID_COLOR:
135 if (node->m_style.solidColor)
136 return node->m_style.solidColor;
137 break;
138 case QSvgStyleProperty::GRADIENT:
139 if (node->m_style.gradient)
140 return node->m_style.gradient;
141 break;
142 case QSvgStyleProperty::TRANSFORM:
143 if (node->m_style.transform)
144 return node->m_style.transform;
145 break;
146 case QSvgStyleProperty::ANIMATE_COLOR:
147 if (node->m_style.animateColor)
148 return node->m_style.animateColor;
149 break;
150 case QSvgStyleProperty::ANIMATE_TRANSFORM:
151 if (!node->m_style.animateTransforms.isEmpty())
152 return node->m_style.animateTransforms.first();
153 break;
154 case QSvgStyleProperty::OPACITY:
155 if (node->m_style.opacity)
156 return node->m_style.opacity;
157 break;
158 case QSvgStyleProperty::COMP_OP:
159 if (node->m_style.compop)
160 return node->m_style.compop;
161 break;
162 default:
163 break;
164 }
165 node = node->parent();
166 }
167
168 return 0;
169}
170
171QSvgFillStyleProperty * QSvgNode::styleProperty(const QString &id) const
172{
173 QString rid = id;
174 if (rid.startsWith(c: QLatin1Char('#')))
175 rid.remove(i: 0, len: 1);
176 QSvgTinyDocument *doc = document();
177 return doc ? doc->namedStyle(id: rid) : 0;
178}
179
180QRectF QSvgNode::fastBounds(QPainter *p, QSvgExtraStates &states) const
181{
182 return bounds(p, states);
183}
184
185QRectF QSvgNode::bounds(QPainter *, QSvgExtraStates &) const
186{
187 return QRectF(0, 0, 0, 0);
188}
189
190QRectF QSvgNode::transformedBounds() const
191{
192 if (!m_cachedBounds.isEmpty())
193 return m_cachedBounds;
194
195 QImage dummy(1, 1, QImage::Format_RGB32);
196 QPainter p(&dummy);
197 QSvgExtraStates states;
198
199 QPen pen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
200 pen.setMiterLimit(4);
201 p.setPen(pen);
202
203 QStack<QSvgNode*> parentApplyStack;
204 QSvgNode *parent = m_parent;
205 while (parent) {
206 parentApplyStack.push(t: parent);
207 parent = parent->parent();
208 }
209
210 for (int i = parentApplyStack.size() - 1; i >= 0; --i)
211 parentApplyStack[i]->applyStyle(p: &p, states);
212
213 p.setWorldTransform(matrix: QTransform());
214
215 m_cachedBounds = transformedBounds(p: &p, states);
216 return m_cachedBounds;
217}
218
219QSvgTinyDocument * QSvgNode::document() const
220{
221 QSvgTinyDocument *doc = nullptr;
222 QSvgNode *node = const_cast<QSvgNode*>(this);
223 while (node && node->type() != QSvgNode::DOC) {
224 node = node->parent();
225 }
226 doc = static_cast<QSvgTinyDocument*>(node);
227
228 return doc;
229}
230
231void QSvgNode::setRequiredFeatures(const QStringList &lst)
232{
233 m_requiredFeatures = lst;
234}
235
236const QStringList & QSvgNode::requiredFeatures() const
237{
238 return m_requiredFeatures;
239}
240
241void QSvgNode::setRequiredExtensions(const QStringList &lst)
242{
243 m_requiredExtensions = lst;
244}
245
246const QStringList & QSvgNode::requiredExtensions() const
247{
248 return m_requiredExtensions;
249}
250
251void QSvgNode::setRequiredLanguages(const QStringList &lst)
252{
253 m_requiredLanguages = lst;
254}
255
256const QStringList & QSvgNode::requiredLanguages() const
257{
258 return m_requiredLanguages;
259}
260
261void QSvgNode::setRequiredFormats(const QStringList &lst)
262{
263 m_requiredFormats = lst;
264}
265
266const QStringList & QSvgNode::requiredFormats() const
267{
268 return m_requiredFormats;
269}
270
271void QSvgNode::setRequiredFonts(const QStringList &lst)
272{
273 m_requiredFonts = lst;
274}
275
276const QStringList & QSvgNode::requiredFonts() const
277{
278 return m_requiredFonts;
279}
280
281void QSvgNode::setVisible(bool visible)
282{
283 //propagate visibility change of true to the parent
284 //not propagating false is just a small performance
285 //degradation since we'll iterate over children without
286 //drawing any of them
287 if (m_parent && visible && !m_parent->isVisible())
288 m_parent->setVisible(true);
289
290 m_visible = visible;
291}
292
293QRectF QSvgNode::transformedBounds(QPainter *p, QSvgExtraStates &states) const
294{
295 applyStyle(p, states);
296 QRectF rect = bounds(p, states);
297 revertStyle(p, states);
298 return rect;
299}
300
301void QSvgNode::setNodeId(const QString &i)
302{
303 m_id = i;
304}
305
306void QSvgNode::setXmlClass(const QString &str)
307{
308 m_class = str;
309}
310
311void QSvgNode::setDisplayMode(DisplayMode mode)
312{
313 m_displayMode = mode;
314}
315
316QSvgNode::DisplayMode QSvgNode::displayMode() const
317{
318 return m_displayMode;
319}
320
321qreal QSvgNode::strokeWidth(QPainter *p)
322{
323 const QPen &pen = p->pen();
324 if (pen.style() == Qt::NoPen || pen.brush().style() == Qt::NoBrush || pen.isCosmetic())
325 return 0;
326 return pen.widthF();
327}
328
329bool QSvgNode::shouldDrawNode(QPainter *p, QSvgExtraStates &states) const
330{
331 static bool alwaysDraw = qEnvironmentVariableIntValue(varName: "QT_SVG_DISABLE_SIZE_LIMIT");
332 if (alwaysDraw)
333 return true;
334
335 QRectF brect = fastBounds(p, states);
336 if (brect.width() <= QT_SVG_SIZE_LIMIT && brect.height() <= QT_SVG_SIZE_LIMIT) {
337 return true;
338 } else {
339 qCWarning(lcSvgDraw) << "Shape of type" << type() << "ignored because it will take too long to rasterize (bounding rect=" << brect << ")."
340 << "Set QT_SVG_DISABLE_SIZE_LIMIT=1 to disable this check.";
341 return false;
342 }
343}
344
345QT_END_NAMESPACE
346

source code of qtsvg/src/svg/qsvgnode.cpp