Coverage Report

Created: 2025-07-23 06:48

/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