/src/qt/qtsvg/src/svg/qsvgnode.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | #include "qsvggraphics_p.h" |
7 | | |
8 | | #include <QLoggingCategory> |
9 | | #include<QElapsedTimer> |
10 | | #include <QtGui/qimageiohandler.h> |
11 | | |
12 | | #include "qdebug.h" |
13 | | #include "qstack.h" |
14 | | |
15 | | #include <QtGui/private/qoutlinemapper_p.h> |
16 | | |
17 | | QT_BEGIN_NAMESPACE |
18 | | |
19 | | #ifndef QT_NO_DEBUG |
20 | | Q_STATIC_LOGGING_CATEGORY(lcSvgTiming, "qt.svg.timing") |
21 | | #endif |
22 | | |
23 | | #if !defined(QT_SVG_SIZE_LIMIT) |
24 | 1.18M | # define QT_SVG_SIZE_LIMIT QT_RASTER_COORD_LIMIT |
25 | | #endif |
26 | | |
27 | | QSvgNode::QSvgNode(QSvgNode *parent) |
28 | 1.12M | : m_parent(parent), |
29 | 1.12M | m_displayMode(BlockMode), |
30 | 1.12M | m_visible(true) |
31 | 1.12M | { |
32 | 1.12M | } |
33 | | |
34 | | QSvgNode::~QSvgNode() |
35 | 1.12M | { |
36 | | |
37 | 1.12M | } |
38 | | |
39 | | void QSvgNode::draw(QPainter *p, QSvgExtraStates &states) |
40 | 482k | { |
41 | | #ifndef QT_NO_DEBUG |
42 | | QElapsedTimer qtSvgTimer; qtSvgTimer.start(); |
43 | | #endif |
44 | | |
45 | 482k | if (shouldDrawNode(p, states)) { |
46 | 472k | applyStyle(p, states); |
47 | 472k | applyAnimatedStyle(p, states); |
48 | 472k | QSvgNode *maskNode = this->hasMask() ? document()->namedNode(this->maskId()) : nullptr; |
49 | 472k | QSvgFilterContainer *filterNode = this->hasFilter() ? static_cast<QSvgFilterContainer*>(document()->namedNode(this->filterId())) |
50 | 472k | : nullptr; |
51 | 472k | if (filterNode && filterNode->type() == QSvgNode::Filter && filterNode->supported()) { |
52 | 3 | QTransform xf = p->transform(); |
53 | 3 | p->resetTransform(); |
54 | 3 | QRectF localRect = internalBounds(p, states); |
55 | 3 | p->setTransform(xf); |
56 | 3 | QRectF boundsRect = xf.mapRect(filterNode->filterRegion(localRect)); |
57 | 3 | QImage proxy = drawIntoBuffer(p, states, boundsRect.toRect()); |
58 | 3 | proxy = filterNode->applyFilter(proxy, p, localRect); |
59 | 3 | if (maskNode && maskNode->type() == QSvgNode::Mask) { |
60 | 0 | boundsRect = QRectF(proxy.offset(), proxy.size()); |
61 | 0 | localRect = p->transform().inverted().mapRect(boundsRect); |
62 | 0 | QImage mask = static_cast<QSvgMask*>(maskNode)->createMask(p, states, localRect, &boundsRect); |
63 | 0 | applyMaskToBuffer(&proxy, mask); |
64 | 0 | } |
65 | 3 | applyBufferToCanvas(p, proxy); |
66 | | |
67 | 472k | } else if (maskNode && maskNode->type() == QSvgNode::Mask) { |
68 | 0 | QRectF boundsRect; |
69 | 0 | QImage mask = static_cast<QSvgMask*>(maskNode)->createMask(p, states, this, &boundsRect); |
70 | 0 | drawWithMask(p, states, mask, boundsRect.toRect()); |
71 | 472k | } else if (!qFuzzyCompare(p->opacity(), qreal(1.0)) && requiresGroupRendering()) { |
72 | 0 | QTransform xf = p->transform(); |
73 | 0 | p->resetTransform(); |
74 | |
|
75 | 0 | QRectF localRect = decoratedInternalBounds(p, states); |
76 | | // adding safety border needed because of the antialiazing effects |
77 | 0 | QRectF boundsRect = xf.mapRect(localRect); |
78 | 0 | const int deltaX = boundsRect.width() * 0.1; |
79 | 0 | const int deltaY = boundsRect.height() * 0.1; |
80 | 0 | boundsRect = boundsRect.adjusted(-deltaX, -deltaY, deltaX, deltaY); |
81 | |
|
82 | 0 | p->setTransform(xf); |
83 | |
|
84 | 0 | QImage proxy = drawIntoBuffer(p, states, boundsRect.toAlignedRect()); |
85 | 0 | applyBufferToCanvas(p, proxy); |
86 | 472k | } else { |
87 | 472k | if (separateFillStroke()) |
88 | 161k | fillThenStroke(p, states); |
89 | 311k | else |
90 | 311k | drawCommand(p, states); |
91 | | |
92 | 472k | } |
93 | 472k | revertAnimatedStyle(p ,states); |
94 | 472k | revertStyle(p, states); |
95 | 472k | } |
96 | | |
97 | | #ifndef QT_NO_DEBUG |
98 | | if (Q_UNLIKELY(lcSvgTiming().isDebugEnabled())) |
99 | | qCDebug(lcSvgTiming) << "Drawing" << typeName() << "took" << (qtSvgTimer.nsecsElapsed() / 1000000.0f) << "ms"; |
100 | | #endif |
101 | 482k | } |
102 | | |
103 | | void QSvgNode::fillThenStroke(QPainter *p, QSvgExtraStates &states) |
104 | 161k | { |
105 | 161k | qreal oldOpacity = p->opacity(); |
106 | 161k | if (p->brush().style() != Qt::NoBrush) { |
107 | 160k | QPen oldPen = p->pen(); |
108 | 160k | p->setPen(Qt::NoPen); |
109 | 160k | p->setOpacity(oldOpacity * states.fillOpacity); |
110 | | |
111 | 160k | drawCommand(p, states); |
112 | | |
113 | 160k | p->setPen(oldPen); |
114 | 160k | } |
115 | 161k | if (p->pen() != Qt::NoPen && p->pen().brush() != Qt::NoBrush && p->pen().widthF() != 0) { |
116 | 100k | QBrush oldBrush = p->brush(); |
117 | 100k | p->setOpacity(oldOpacity * states.strokeOpacity); |
118 | 100k | p->setBrush(Qt::NoBrush); |
119 | | |
120 | 100k | drawCommand(p, states); |
121 | | |
122 | 100k | p->setBrush(oldBrush); |
123 | 100k | } |
124 | 161k | p->setOpacity(oldOpacity); |
125 | 161k | } |
126 | | |
127 | | void QSvgNode::drawWithMask(QPainter *p, QSvgExtraStates &states, const QImage &mask, const QRect &boundsRect) |
128 | 0 | { |
129 | 0 | QImage proxy = drawIntoBuffer(p, states, boundsRect); |
130 | 0 | if (proxy.isNull()) |
131 | 0 | return; |
132 | 0 | applyMaskToBuffer(&proxy, mask); |
133 | |
|
134 | 0 | p->save(); |
135 | 0 | p->resetTransform(); |
136 | 0 | p->drawImage(boundsRect, proxy); |
137 | 0 | p->restore(); |
138 | 0 | } |
139 | | |
140 | | QImage QSvgNode::drawIntoBuffer(QPainter *p, QSvgExtraStates &states, const QRect &boundsRect) |
141 | 3 | { |
142 | 3 | QImage proxy; |
143 | 3 | if (!QImageIOHandler::allocateImage(boundsRect.size(), QImage::Format_ARGB32_Premultiplied, &proxy)) { |
144 | 3 | qCWarning(lcSvgDraw) << "The requested buffer size is too big, ignoring"; |
145 | 3 | return proxy; |
146 | 3 | } |
147 | 0 | proxy.setOffset(boundsRect.topLeft()); |
148 | 0 | proxy.fill(Qt::transparent); |
149 | 0 | QPainter proxyPainter(&proxy); |
150 | 0 | proxyPainter.setPen(p->pen()); |
151 | 0 | proxyPainter.setBrush(p->brush()); |
152 | 0 | proxyPainter.setFont(p->font()); |
153 | 0 | proxyPainter.translate(-boundsRect.topLeft()); |
154 | 0 | proxyPainter.setTransform(p->transform(), true); |
155 | 0 | proxyPainter.setRenderHints(p->renderHints()); |
156 | 0 | if (separateFillStroke()) |
157 | 0 | fillThenStroke(&proxyPainter, states); |
158 | 0 | else |
159 | 0 | drawCommand(&proxyPainter, states); |
160 | 0 | return proxy; |
161 | 3 | } |
162 | | |
163 | | void QSvgNode::applyMaskToBuffer(QImage *proxy, QImage mask) const |
164 | 0 | { |
165 | 0 | QPainter proxyPainter(proxy); |
166 | 0 | proxyPainter.setCompositionMode(QPainter::CompositionMode_DestinationOut); |
167 | 0 | proxyPainter.resetTransform(); |
168 | 0 | proxyPainter.drawImage(QRect(0, 0, mask.width(), mask.height()), mask); |
169 | 0 | } |
170 | | |
171 | | void QSvgNode::applyBufferToCanvas(QPainter *p, QImage proxy) const |
172 | 3 | { |
173 | 3 | QTransform xf = p->transform(); |
174 | 3 | p->resetTransform(); |
175 | 3 | p->drawImage(QRect(proxy.offset(), proxy.size()), proxy); |
176 | 3 | p->setTransform(xf); |
177 | 3 | } |
178 | | |
179 | | bool QSvgNode::isDescendantOf(const QSvgNode *parent) const |
180 | 1.96M | { |
181 | 1.96M | const QSvgNode *n = this; |
182 | 6.59M | while (n) { |
183 | 4.62M | if (n == parent) |
184 | 85 | return true; |
185 | 4.62M | n = n->m_parent; |
186 | 4.62M | } |
187 | 1.96M | return false; |
188 | 1.96M | } |
189 | | |
190 | | void QSvgNode::appendStyleProperty(QSvgStyleProperty *prop, const QString &id) |
191 | 249k | { |
192 | | //qDebug()<<"appending "<<prop->type()<< " ("<< id <<") "<<"to "<<this<<this->type(); |
193 | 249k | QSvgTinyDocument *doc; |
194 | 249k | switch (prop->type()) { |
195 | 16 | case QSvgStyleProperty::QUALITY: |
196 | 16 | m_style.quality = static_cast<QSvgQualityStyle*>(prop); |
197 | 16 | break; |
198 | 45.5k | case QSvgStyleProperty::FILL: |
199 | 45.5k | m_style.fill = static_cast<QSvgFillStyle*>(prop); |
200 | 45.5k | break; |
201 | 0 | case QSvgStyleProperty::VIEWPORT_FILL: |
202 | 0 | m_style.viewportFill = static_cast<QSvgViewportFillStyle*>(prop); |
203 | 0 | break; |
204 | 100k | case QSvgStyleProperty::FONT: |
205 | 100k | m_style.font = static_cast<QSvgFontStyle*>(prop); |
206 | 100k | break; |
207 | 77.2k | case QSvgStyleProperty::STROKE: |
208 | 77.2k | m_style.stroke = static_cast<QSvgStrokeStyle*>(prop); |
209 | 77.2k | break; |
210 | 0 | case QSvgStyleProperty::SOLID_COLOR: |
211 | 0 | m_style.solidColor = static_cast<QSvgSolidColorStyle*>(prop); |
212 | 0 | doc = document(); |
213 | 0 | if (doc && !id.isEmpty()) |
214 | 0 | doc->addNamedStyle(id, m_style.solidColor); |
215 | 0 | break; |
216 | 12.0k | case QSvgStyleProperty::GRADIENT: |
217 | 12.0k | m_style.gradient = static_cast<QSvgGradientStyle*>(prop); |
218 | 12.0k | doc = document(); |
219 | 12.0k | if (doc && !id.isEmpty()) |
220 | 10.5k | doc->addNamedStyle(id, m_style.gradient); |
221 | 12.0k | break; |
222 | 1.03k | case QSvgStyleProperty::PATTERN: |
223 | 1.03k | m_style.pattern = static_cast<QSvgPatternStyle*>(prop); |
224 | 1.03k | doc = document(); |
225 | 1.03k | if (doc && !id.isEmpty()) |
226 | 689 | doc->addNamedStyle(id, m_style.pattern); |
227 | 1.03k | break; |
228 | 4.87k | case QSvgStyleProperty::TRANSFORM: |
229 | 4.87k | m_style.transform = static_cast<QSvgTransformStyle*>(prop); |
230 | 4.87k | break; |
231 | 7.74k | case QSvgStyleProperty::OPACITY: |
232 | 7.74k | m_style.opacity = static_cast<QSvgOpacityStyle*>(prop); |
233 | 7.74k | break; |
234 | 0 | case QSvgStyleProperty::COMP_OP: |
235 | 0 | m_style.compop = static_cast<QSvgCompOpStyle*>(prop); |
236 | 0 | break; |
237 | 0 | default: |
238 | 0 | qDebug("QSvgNode: Trying to append unknown property!"); |
239 | 0 | break; |
240 | 249k | } |
241 | 249k | } |
242 | | |
243 | | void QSvgNode::applyStyle(QPainter *p, QSvgExtraStates &states) const |
244 | 5.10M | { |
245 | 5.10M | m_style.apply(p, this, states); |
246 | 5.10M | } |
247 | | |
248 | | /*! |
249 | | \internal |
250 | | |
251 | | Apply the styles of all parents to the painter and the states. |
252 | | The styles are applied from the top level node to the current node. |
253 | | This function can be used to set the correct style for a node |
254 | | if it's draw function is triggered out of the ordinary draw context, |
255 | | for example the mask node, that is cross-referenced. |
256 | | */ |
257 | | void QSvgNode::applyStyleRecursive(QPainter *p, QSvgExtraStates &states) const |
258 | 7.72k | { |
259 | 7.72k | if (parent()) |
260 | 3.86k | parent()->applyStyleRecursive(p, states); |
261 | 7.72k | applyStyle(p, states); |
262 | 7.72k | } |
263 | | |
264 | | void QSvgNode::revertStyle(QPainter *p, QSvgExtraStates &states) const |
265 | 5.10M | { |
266 | 5.10M | m_style.revert(p, states); |
267 | 5.10M | } |
268 | | |
269 | | void QSvgNode::revertStyleRecursive(QPainter *p, QSvgExtraStates &states) const |
270 | 7.72k | { |
271 | 7.72k | revertStyle(p, states); |
272 | 7.72k | if (parent()) |
273 | 3.86k | parent()->revertStyleRecursive(p, states); |
274 | 7.72k | } |
275 | | |
276 | | void QSvgNode::applyAnimatedStyle(QPainter *p, QSvgExtraStates &states) const |
277 | 472k | { |
278 | 472k | if (document()->animated()) |
279 | 199 | m_animatedStyle.apply(p, this, states); |
280 | 472k | } |
281 | | |
282 | | void QSvgNode::revertAnimatedStyle(QPainter *p, QSvgExtraStates &states) const |
283 | 472k | { |
284 | 472k | if (document()->animated()) |
285 | 199 | m_animatedStyle.revert(p, states); |
286 | 472k | } |
287 | | |
288 | | QSvgStyleProperty * QSvgNode::styleProperty(QSvgStyleProperty::Type type) const |
289 | 120M | { |
290 | 120M | const QSvgNode *node = this; |
291 | 393M | while (node) { |
292 | 345M | switch (type) { |
293 | 0 | case QSvgStyleProperty::QUALITY: |
294 | 0 | if (node->m_style.quality) |
295 | 0 | return node->m_style.quality; |
296 | 0 | break; |
297 | 172M | case QSvgStyleProperty::FILL: |
298 | 172M | if (node->m_style.fill) |
299 | 37.2M | return node->m_style.fill; |
300 | 135M | break; |
301 | 135M | case QSvgStyleProperty::VIEWPORT_FILL: |
302 | 0 | if (m_style.viewportFill) |
303 | 0 | return node->m_style.viewportFill; |
304 | 0 | break; |
305 | 1.53M | case QSvgStyleProperty::FONT: |
306 | 1.53M | if (node->m_style.font) |
307 | 447k | return node->m_style.font; |
308 | 1.08M | break; |
309 | 171M | case QSvgStyleProperty::STROKE: |
310 | 171M | if (node->m_style.stroke) |
311 | 35.1M | return node->m_style.stroke; |
312 | 136M | break; |
313 | 136M | case QSvgStyleProperty::SOLID_COLOR: |
314 | 0 | if (node->m_style.solidColor) |
315 | 0 | return node->m_style.solidColor; |
316 | 0 | break; |
317 | 0 | case QSvgStyleProperty::GRADIENT: |
318 | 0 | if (node->m_style.gradient) |
319 | 0 | return node->m_style.gradient; |
320 | 0 | break; |
321 | 0 | case QSvgStyleProperty::PATTERN: |
322 | 0 | if (node->m_style.pattern) |
323 | 0 | return node->m_style.pattern; |
324 | 0 | break; |
325 | 0 | case QSvgStyleProperty::TRANSFORM: |
326 | 0 | if (node->m_style.transform) |
327 | 0 | return node->m_style.transform; |
328 | 0 | break; |
329 | 0 | case QSvgStyleProperty::OPACITY: |
330 | 0 | if (node->m_style.opacity) |
331 | 0 | return node->m_style.opacity; |
332 | 0 | break; |
333 | 0 | case QSvgStyleProperty::COMP_OP: |
334 | 0 | if (node->m_style.compop) |
335 | 0 | return node->m_style.compop; |
336 | 0 | break; |
337 | 0 | default: |
338 | 0 | break; |
339 | 345M | } |
340 | 272M | node = node->parent(); |
341 | 272M | } |
342 | | |
343 | 47.7M | return 0; |
344 | 120M | } |
345 | | |
346 | | QSvgPaintStyleProperty *QSvgNode::styleProperty(QStringView id) const |
347 | 39.6k | { |
348 | 39.6k | if (id.startsWith(QLatin1Char('#'))) |
349 | 29.0k | id.slice(1); |
350 | 39.6k | QSvgTinyDocument *doc = document(); |
351 | 39.6k | return doc ? doc->namedStyle(id.toString()) : 0; |
352 | 39.6k | } |
353 | | |
354 | | QRectF QSvgNode::internalFastBounds(QPainter *p, QSvgExtraStates &states) const |
355 | 219k | { |
356 | 219k | return internalBounds(p, states); |
357 | 219k | } |
358 | | |
359 | | QRectF QSvgNode::internalBounds(QPainter *, QSvgExtraStates &) const |
360 | 0 | { |
361 | 0 | return QRectF(0, 0, 0, 0); |
362 | 0 | } |
363 | | |
364 | | QRectF QSvgNode::bounds() const |
365 | 33.0k | { |
366 | 33.0k | if (!m_cachedBounds.isEmpty()) |
367 | 0 | return m_cachedBounds; |
368 | | |
369 | 33.0k | QImage dummy(1, 1, QImage::Format_RGB32); |
370 | 33.0k | QPainter p(&dummy); |
371 | 33.0k | initPainter(&p); |
372 | 33.0k | QSvgExtraStates states; |
373 | | |
374 | 33.0k | if (parent()) |
375 | 0 | parent()->applyStyleRecursive(&p, states); |
376 | 33.0k | p.setWorldTransform(QTransform()); |
377 | 33.0k | m_cachedBounds = bounds(&p, states); |
378 | 33.0k | if (parent()) // always revert the style to not store old transformations |
379 | 0 | parent()->revertStyleRecursive(&p, states); |
380 | 33.0k | return m_cachedBounds; |
381 | 33.0k | } |
382 | | |
383 | | QSvgTinyDocument * QSvgNode::document() const |
384 | 3.89M | { |
385 | 3.89M | QSvgTinyDocument *doc = nullptr; |
386 | 3.89M | QSvgNode *node = const_cast<QSvgNode*>(this); |
387 | 15.9M | while (node && node->type() != QSvgNode::Doc) { |
388 | 12.0M | node = node->parent(); |
389 | 12.0M | } |
390 | 3.89M | doc = static_cast<QSvgTinyDocument*>(node); |
391 | | |
392 | 3.89M | return doc; |
393 | 3.89M | } |
394 | | |
395 | | QString QSvgNode::typeName() const |
396 | 22.9M | { |
397 | 22.9M | switch (type()) { |
398 | 34.4k | case Doc: return QStringLiteral("svg"); |
399 | 16.2M | case Group: return QStringLiteral("g"); |
400 | 583 | case Defs: return QStringLiteral("defs"); |
401 | 212k | case Switch: return QStringLiteral("switch"); |
402 | 0 | case AnimateColor: return QStringLiteral("animateColor"); |
403 | 0 | case AnimateTransform: return QStringLiteral("animateTransform"); |
404 | 3 | case Circle: return QStringLiteral("circle"); |
405 | 1.48k | case Ellipse: return QStringLiteral("ellipse"); |
406 | 0 | case Image: return QStringLiteral("image"); |
407 | 78 | case Line: return QStringLiteral("line"); |
408 | 6.29M | case Path: return QStringLiteral("path"); |
409 | 214 | case Polygon: return QStringLiteral("polygon"); |
410 | 1.25k | case Polyline: return QStringLiteral("polyline"); |
411 | 58 | case Rect: return QStringLiteral("rect"); |
412 | 8.15k | case Text: return QStringLiteral("text"); |
413 | 59 | case Textarea: return QStringLiteral("textarea"); |
414 | 1.55k | case Tspan: return QStringLiteral("tspan"); |
415 | 145k | case Use: return QStringLiteral("use"); |
416 | 0 | case Video: return QStringLiteral("video"); |
417 | 134 | case Mask: return QStringLiteral("mask"); |
418 | 6.25k | case Symbol: return QStringLiteral("symbol"); |
419 | 4.05k | case Marker: return QStringLiteral("marker"); |
420 | 1.29k | case Pattern: return QStringLiteral("pattern"); |
421 | 299 | case Filter: return QStringLiteral("filter"); |
422 | 0 | case FeMerge: return QStringLiteral("feMerge"); |
423 | 0 | case FeMergenode: return QStringLiteral("feMergeNode"); |
424 | 0 | case FeColormatrix: return QStringLiteral("feColorMatrix"); |
425 | 0 | case FeGaussianblur: return QStringLiteral("feGaussianBlur"); |
426 | 0 | case FeOffset: return QStringLiteral("feOffset"); |
427 | 0 | case FeComposite: return QStringLiteral("feComposite"); |
428 | 0 | case FeFlood: return QStringLiteral("feFlood"); |
429 | 0 | case FeBlend: return QStringLiteral("feBlend"); |
430 | 43.3k | case FeUnsupported: return QStringLiteral("feUnsupported"); |
431 | 22.9M | } |
432 | 0 | return QStringLiteral("unknown"); |
433 | 22.9M | } |
434 | | |
435 | | void QSvgNode::setRequiredFeatures(const QStringList &lst) |
436 | 845k | { |
437 | 845k | m_requiredFeatures = lst; |
438 | 845k | } |
439 | | |
440 | | const QStringList & QSvgNode::requiredFeatures() const |
441 | 1.27k | { |
442 | 1.27k | return m_requiredFeatures; |
443 | 1.27k | } |
444 | | |
445 | | void QSvgNode::setRequiredExtensions(const QStringList &lst) |
446 | 845k | { |
447 | 845k | m_requiredExtensions = lst; |
448 | 845k | } |
449 | | |
450 | | const QStringList & QSvgNode::requiredExtensions() const |
451 | 1.27k | { |
452 | 1.27k | return m_requiredExtensions; |
453 | 1.27k | } |
454 | | |
455 | | void QSvgNode::setRequiredLanguages(const QStringList &lst) |
456 | 845k | { |
457 | 845k | m_requiredLanguages = lst; |
458 | 845k | } |
459 | | |
460 | | const QStringList & QSvgNode::requiredLanguages() const |
461 | 1.27k | { |
462 | 1.27k | return m_requiredLanguages; |
463 | 1.27k | } |
464 | | |
465 | | void QSvgNode::setRequiredFormats(const QStringList &lst) |
466 | 845k | { |
467 | 845k | m_requiredFormats = lst; |
468 | 845k | } |
469 | | |
470 | | const QStringList & QSvgNode::requiredFormats() const |
471 | 1.27k | { |
472 | 1.27k | return m_requiredFormats; |
473 | 1.27k | } |
474 | | |
475 | | void QSvgNode::setRequiredFonts(const QStringList &lst) |
476 | 845k | { |
477 | 845k | m_requiredFonts = lst; |
478 | 845k | } |
479 | | |
480 | | const QStringList & QSvgNode::requiredFonts() const |
481 | 1.27k | { |
482 | 1.27k | return m_requiredFonts; |
483 | 1.27k | } |
484 | | |
485 | | void QSvgNode::setVisible(bool visible) |
486 | 845k | { |
487 | | //propagate visibility change of true to the parent |
488 | | //not propagating false is just a small performance |
489 | | //degradation since we'll iterate over children without |
490 | | //drawing any of them |
491 | 845k | if (m_parent && visible && !m_parent->isVisible()) |
492 | 0 | m_parent->setVisible(true); |
493 | | |
494 | 845k | m_visible = visible; |
495 | 845k | } |
496 | | |
497 | | QRectF QSvgNode::bounds(QPainter *p, QSvgExtraStates &states) const |
498 | 4.02M | { |
499 | 4.02M | applyStyle(p, states); |
500 | 4.02M | QRectF rect = internalBounds(p, states); |
501 | 4.02M | revertStyle(p, states); |
502 | 4.02M | return rect; |
503 | 4.02M | } |
504 | | |
505 | | QRectF QSvgNode::decoratedInternalBounds(QPainter *p, QSvgExtraStates &states) const |
506 | 0 | { |
507 | 0 | return filterRegion(internalBounds(p, states)); |
508 | 0 | } |
509 | | |
510 | | QRectF QSvgNode::decoratedBounds(QPainter *p, QSvgExtraStates &states) const |
511 | 0 | { |
512 | 0 | applyStyle(p, states); |
513 | 0 | QRectF rect = decoratedInternalBounds(p, states); |
514 | 0 | revertStyle(p, states); |
515 | 0 | return rect; |
516 | 0 | } |
517 | | |
518 | | void QSvgNode::setNodeId(const QString &i) |
519 | 859k | { |
520 | 859k | m_id = i; |
521 | 859k | } |
522 | | |
523 | | void QSvgNode::setXmlClass(const QString &str) |
524 | 859k | { |
525 | 859k | m_class = str; |
526 | 859k | } |
527 | | |
528 | | QString QSvgNode::maskId() const |
529 | 0 | { |
530 | 0 | return m_maskId; |
531 | 0 | } |
532 | | |
533 | | void QSvgNode::setMaskId(const QString &str) |
534 | 0 | { |
535 | 0 | m_maskId = str; |
536 | 0 | } |
537 | | |
538 | | bool QSvgNode::hasMask() const |
539 | 472k | { |
540 | 472k | if (document()->options().testFlag(QtSvg::Tiny12FeaturesOnly)) |
541 | 0 | return false; |
542 | 472k | return !m_maskId.isEmpty(); |
543 | 472k | } |
544 | | |
545 | | QString QSvgNode::filterId() const |
546 | 494 | { |
547 | 494 | return m_filterId; |
548 | 494 | } |
549 | | |
550 | | void QSvgNode::setFilterId(const QString &str) |
551 | 756 | { |
552 | 756 | m_filterId = str; |
553 | 756 | } |
554 | | |
555 | | bool QSvgNode::hasFilter() const |
556 | 472k | { |
557 | 472k | if (document()->options().testFlag(QtSvg::Tiny12FeaturesOnly)) |
558 | 0 | return false; |
559 | 472k | return !m_filterId.isEmpty(); |
560 | 472k | } |
561 | | |
562 | | QString QSvgNode::markerStartId() const |
563 | 0 | { |
564 | 0 | return m_markerStartId; |
565 | 0 | } |
566 | | |
567 | | void QSvgNode::setMarkerStartId(const QString &str) |
568 | 0 | { |
569 | 0 | m_markerStartId = str; |
570 | 0 | } |
571 | | |
572 | | bool QSvgNode::hasMarkerStart() const |
573 | 259k | { |
574 | 259k | if (document()->options().testFlag(QtSvg::Tiny12FeaturesOnly)) |
575 | 0 | return false; |
576 | 259k | return !m_markerStartId.isEmpty(); |
577 | 259k | } |
578 | | |
579 | | QString QSvgNode::markerMidId() const |
580 | 0 | { |
581 | 0 | return m_markerMidId; |
582 | 0 | } |
583 | | |
584 | | void QSvgNode::setMarkerMidId(const QString &str) |
585 | 0 | { |
586 | 0 | m_markerMidId = str; |
587 | 0 | } |
588 | | |
589 | | bool QSvgNode::hasMarkerMid() const |
590 | 259k | { |
591 | 259k | if (document()->options().testFlag(QtSvg::Tiny12FeaturesOnly)) |
592 | 0 | return false; |
593 | 259k | return !m_markerMidId.isEmpty(); |
594 | 259k | } |
595 | | |
596 | | QString QSvgNode::markerEndId() const |
597 | 872 | { |
598 | 872 | return m_markerEndId; |
599 | 872 | } |
600 | | |
601 | | void QSvgNode::setMarkerEndId(const QString &str) |
602 | 718 | { |
603 | 718 | m_markerEndId = str; |
604 | 718 | } |
605 | | |
606 | | bool QSvgNode::hasMarkerEnd() const |
607 | 259k | { |
608 | 259k | if (document()->options().testFlag(QtSvg::Tiny12FeaturesOnly)) |
609 | 0 | return false; |
610 | 259k | return !m_markerEndId.isEmpty(); |
611 | 259k | } |
612 | | |
613 | | bool QSvgNode::hasAnyMarker() const |
614 | 258k | { |
615 | 258k | if (document()->options().testFlag(QtSvg::Tiny12FeaturesOnly)) |
616 | 0 | return false; |
617 | 258k | return hasMarkerStart() || hasMarkerMid() || hasMarkerEnd(); |
618 | 258k | } |
619 | | |
620 | | bool QSvgNode::requiresGroupRendering() const |
621 | 0 | { |
622 | 0 | return false; |
623 | 0 | } |
624 | | |
625 | | void QSvgNode::setDisplayMode(DisplayMode mode) |
626 | 3.56k | { |
627 | 3.56k | m_displayMode = mode; |
628 | 3.56k | } |
629 | | |
630 | | QSvgNode::DisplayMode QSvgNode::displayMode() const |
631 | 361k | { |
632 | 361k | return m_displayMode; |
633 | 361k | } |
634 | | |
635 | | qreal QSvgNode::strokeWidth(QPainter *p) |
636 | 1.07M | { |
637 | 1.07M | const QPen &pen = p->pen(); |
638 | 1.07M | if (pen.style() == Qt::NoPen || pen.brush().style() == Qt::NoBrush || pen.isCosmetic()) |
639 | 308k | return 0; |
640 | 765k | return pen.widthF(); |
641 | 1.07M | } |
642 | | |
643 | | void QSvgNode::initPainter(QPainter *p) |
644 | 45.9k | { |
645 | 45.9k | QPen pen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin); |
646 | 45.9k | pen.setMiterLimit(4); |
647 | 45.9k | p->setPen(pen); |
648 | 45.9k | p->setBrush(Qt::black); |
649 | 45.9k | p->setRenderHint(QPainter::Antialiasing); |
650 | 45.9k | p->setRenderHint(QPainter::SmoothPixmapTransform); |
651 | 45.9k | QFont font(p->font()); |
652 | 45.9k | if (font.pointSize() < 0 && font.pixelSize() > 0) { |
653 | 0 | font.setPointSizeF(font.pixelSize() * 72.0 / p->device()->logicalDpiY()); |
654 | 0 | p->setFont(font); |
655 | 0 | } |
656 | 45.9k | } |
657 | | |
658 | | QRectF QSvgNode::boundsOnStroke(QPainter *p, const QPainterPath &path, |
659 | | qreal width, BoundsMode mode) |
660 | 765k | { |
661 | 765k | QPainterPathStroker stroker; |
662 | 765k | stroker.setWidth(width); |
663 | 765k | if (mode == BoundsMode::IncludeMiterLimit) { |
664 | 0 | stroker.setJoinStyle(p->pen().joinStyle()); |
665 | 0 | stroker.setMiterLimit(p->pen().miterLimit()); |
666 | 0 | } |
667 | 765k | QPainterPath stroke = stroker.createStroke(path); |
668 | 765k | return p->transform().map(stroke).boundingRect(); |
669 | 765k | } |
670 | | |
671 | | bool QSvgNode::shouldDrawNode(QPainter *p, QSvgExtraStates &states) const |
672 | 396k | { |
673 | 396k | if (m_displayMode == DisplayMode::NoneMode) |
674 | 0 | return false; |
675 | | |
676 | 396k | if (document() && document()->options().testFlag(QtSvg::AssumeTrustedSource)) |
677 | 0 | return true; |
678 | | |
679 | 396k | QRectF brect = internalFastBounds(p, states); |
680 | 396k | if (brect.width() <= QT_SVG_SIZE_LIMIT && brect.height() <= QT_SVG_SIZE_LIMIT) { |
681 | 388k | return true; |
682 | 388k | } else { |
683 | 7.74k | qCWarning(lcSvgDraw) << "Shape of type" << type() << "ignored because it will take too long to rasterize (bounding rect=" << brect << ")." |
684 | 7.74k | << "Enable AssumeTrustedSource in QSvgHandler or set QT_SVG_DEFAULT_OPTIONS=2 to disable this check."; |
685 | 7.74k | return false; |
686 | 7.74k | } |
687 | 396k | } |
688 | | |
689 | | QRectF QSvgNode::filterRegion(QRectF bounds) const |
690 | 0 | { |
691 | 0 | QSvgFilterContainer *filterNode = hasFilter() |
692 | 0 | ? static_cast<QSvgFilterContainer*>(document()->namedNode(filterId())) |
693 | 0 | : nullptr; |
694 | |
|
695 | 0 | if (filterNode && filterNode->type() == QSvgNode::Filter && filterNode->supported()) |
696 | 0 | return filterNode->filterRegion(bounds); |
697 | | |
698 | 0 | return bounds; |
699 | 0 | } |
700 | | |
701 | | QT_END_NAMESPACE |