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 | |
18 | QT_BEGIN_NAMESPACE |
19 | |
20 | Q_DECLARE_LOGGING_CATEGORY(lcSvgDraw) |
21 | |
22 | QSvgNode::QSvgNode(QSvgNode *parent) |
23 | : m_parent(parent), |
24 | m_visible(true), |
25 | m_displayMode(BlockMode) |
26 | { |
27 | } |
28 | |
29 | QSvgNode::~QSvgNode() |
30 | { |
31 | |
32 | } |
33 | |
34 | bool 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 | |
45 | void 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 | |
99 | void QSvgNode::(QPainter *p, QSvgExtraStates &states) const |
100 | { |
101 | m_style.apply(p, node: this, states); |
102 | } |
103 | |
104 | void QSvgNode::(QPainter *p, QSvgExtraStates &states) const |
105 | { |
106 | m_style.revert(p, states); |
107 | } |
108 | |
109 | QSvgStyleProperty * 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 | |
171 | QSvgFillStyleProperty * 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 | |
180 | QRectF QSvgNode::(QPainter *p, QSvgExtraStates &states) const |
181 | { |
182 | return bounds(p, states); |
183 | } |
184 | |
185 | QRectF QSvgNode::(QPainter *, QSvgExtraStates &) const |
186 | { |
187 | return QRectF(0, 0, 0, 0); |
188 | } |
189 | |
190 | QRectF 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 | |
219 | QSvgTinyDocument * 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 | |
231 | void QSvgNode::setRequiredFeatures(const QStringList &lst) |
232 | { |
233 | m_requiredFeatures = lst; |
234 | } |
235 | |
236 | const QStringList & QSvgNode::requiredFeatures() const |
237 | { |
238 | return m_requiredFeatures; |
239 | } |
240 | |
241 | void QSvgNode::setRequiredExtensions(const QStringList &lst) |
242 | { |
243 | m_requiredExtensions = lst; |
244 | } |
245 | |
246 | const QStringList & QSvgNode::requiredExtensions() const |
247 | { |
248 | return m_requiredExtensions; |
249 | } |
250 | |
251 | void QSvgNode::setRequiredLanguages(const QStringList &lst) |
252 | { |
253 | m_requiredLanguages = lst; |
254 | } |
255 | |
256 | const QStringList & QSvgNode::requiredLanguages() const |
257 | { |
258 | return m_requiredLanguages; |
259 | } |
260 | |
261 | void QSvgNode::setRequiredFormats(const QStringList &lst) |
262 | { |
263 | m_requiredFormats = lst; |
264 | } |
265 | |
266 | const QStringList & QSvgNode::requiredFormats() const |
267 | { |
268 | return m_requiredFormats; |
269 | } |
270 | |
271 | void QSvgNode::setRequiredFonts(const QStringList &lst) |
272 | { |
273 | m_requiredFonts = lst; |
274 | } |
275 | |
276 | const QStringList & QSvgNode::requiredFonts() const |
277 | { |
278 | return m_requiredFonts; |
279 | } |
280 | |
281 | void 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 | |
293 | QRectF QSvgNode::(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 | |
301 | void QSvgNode::setNodeId(const QString &i) |
302 | { |
303 | m_id = i; |
304 | } |
305 | |
306 | void QSvgNode::setXmlClass(const QString &str) |
307 | { |
308 | m_class = str; |
309 | } |
310 | |
311 | void QSvgNode::setDisplayMode(DisplayMode mode) |
312 | { |
313 | m_displayMode = mode; |
314 | } |
315 | |
316 | QSvgNode::DisplayMode QSvgNode::displayMode() const |
317 | { |
318 | return m_displayMode; |
319 | } |
320 | |
321 | qreal 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 | |
329 | bool QSvgNode::(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 | |
345 | QT_END_NAMESPACE |
346 | |