Coverage Report

Created: 2026-02-26 07:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtsvg/src/svg/qsvggraphics.cpp
Line
Count
Source
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
// Qt-Security score:significant reason:default
4
5
6
#include "qsvggraphics_p.h"
7
#include "qsvgstructure_p.h"
8
#include "qsvgfont_p.h"
9
10
#include <qabstracttextdocumentlayout.h>
11
#include <qdebug.h>
12
#include <qloggingcategory.h>
13
#include <qpainter.h>
14
#include <qscopedvaluerollback.h>
15
#include <qtextcursor.h>
16
#include <qtextdocument.h>
17
#include <private/qfixed_p.h>
18
19
#include <QElapsedTimer>
20
#include <QLoggingCategory>
21
22
#include <math.h>
23
24
QT_BEGIN_NAMESPACE
25
26
Q_LOGGING_CATEGORY(lcSvgDraw, "qt.svg.draw")
27
28
#ifndef QT_SVG_MAX_LAYOUT_SIZE
29
0
#define QT_SVG_MAX_LAYOUT_SIZE (qint64(QFIXED_MAX / 2))
30
#endif
31
32
void QSvgDummyNode::drawCommand(QPainter *, QSvgExtraStates &)
33
0
{
34
0
    qWarning("Dummy node not meant to be drawn");
35
0
}
36
37
QSvgEllipse::QSvgEllipse(QSvgNode *parent, const QRectF &rect)
38
0
    : QSvgNode(parent), m_bounds(rect)
39
0
{
40
0
}
41
42
QRectF QSvgEllipse::internalFastBounds(QPainter *p, QSvgExtraStates &) const
43
0
{
44
0
    return p->transform().mapRect(m_bounds);
45
0
}
46
47
QRectF QSvgEllipse::internalBounds(QPainter *p, QSvgExtraStates &) const
48
0
{
49
0
    QPainterPath path;
50
0
    path.addEllipse(m_bounds);
51
0
    qreal sw = strokeWidth(p);
52
0
    return qFuzzyIsNull(sw) ? p->transform().map(path).boundingRect()
53
0
                            : boundsOnStroke(p, path, sw, BoundsMode::Simplistic);
54
0
}
55
56
QRectF QSvgEllipse::decoratedInternalBounds(QPainter *p, QSvgExtraStates &) const
57
0
{
58
0
    QPainterPath path;
59
0
    path.addEllipse(m_bounds);
60
0
    qreal sw = strokeWidth(p);
61
0
    QRectF rect = qFuzzyIsNull(sw) ? p->transform().map(path).boundingRect()
62
0
                                   : boundsOnStroke(p, path, sw, BoundsMode::IncludeMiterLimit);
63
0
    return filterRegion(rect);
64
0
}
65
66
void QSvgEllipse::drawCommand(QPainter *p, QSvgExtraStates &)
67
0
{
68
0
    p->drawEllipse(m_bounds);
69
0
}
70
71
bool QSvgEllipse::separateFillStroke(const QPainter *, const QSvgExtraStates &) const
72
0
{
73
0
    return true;
74
0
}
75
76
QSvgImage::QSvgImage(QSvgNode *parent,
77
                     const QImage &image,
78
                     const QString &filename,
79
                     const QRectF &bounds)
80
0
    : QSvgNode(parent)
81
0
    , m_filename(filename)
82
0
    , m_image(image)
83
0
    , m_bounds(bounds)
84
0
{
85
0
    if (m_bounds.width() == 0.0)
86
0
        m_bounds.setWidth(static_cast<qreal>(m_image.width()));
87
0
    if (m_bounds.height() == 0.0)
88
0
        m_bounds.setHeight(static_cast<qreal>(m_image.height()));
89
0
}
90
91
void QSvgImage::drawCommand(QPainter *p, QSvgExtraStates &)
92
0
{
93
0
    p->drawImage(m_bounds, m_image);
94
0
}
95
96
QSvgLine::QSvgLine(QSvgNode *parent, const QLineF &line)
97
0
    : QSvgNode(parent), m_line(line)
98
0
{
99
0
}
100
101
void QSvgLine::drawCommand(QPainter *p, QSvgExtraStates &states)
102
0
{
103
0
    if (p->pen().widthF() != 0) {
104
0
        qreal oldOpacity = p->opacity();
105
0
        p->setOpacity(oldOpacity * states.strokeOpacity);
106
0
        if (m_line.isNull() && p->pen().capStyle() != Qt::FlatCap)
107
0
            p->drawPoint(m_line.p1());
108
0
        else
109
0
            p->drawLine(m_line);
110
0
        p->setOpacity(oldOpacity);
111
0
    }
112
0
    QSvgMarker::drawMarkersForNode(this, p, states);
113
0
}
114
115
QSvgPath::QSvgPath(QSvgNode *parent, const QPainterPath &qpath)
116
122k
    : QSvgNode(parent), m_path(qpath)
117
122k
{
118
122k
}
119
120
void QSvgPath::drawCommand(QPainter *p, QSvgExtraStates &states)
121
90.4k
{
122
90.4k
    const qreal oldOpacity = p->opacity();
123
90.4k
    const bool drawingInOnePass = !separateFillStroke(p, states);
124
90.4k
    if (drawingInOnePass)
125
16.2k
        p->setOpacity(oldOpacity * states.fillOpacity);
126
90.4k
    m_path.setFillRule(states.fillRule);
127
90.4k
    if (m_path.boundingRect().isNull() && p->pen().capStyle() != Qt::FlatCap)
128
1.02k
        p->drawPoint(m_path.boundingRect().topLeft());
129
89.4k
    else
130
89.4k
        p->drawPath(m_path);
131
90.4k
    if (!path().isEmpty())
132
88.7k
        QSvgMarker::drawMarkersForNode(this, p, states);
133
90.4k
    if (drawingInOnePass)
134
16.2k
        p->setOpacity(oldOpacity);
135
90.4k
}
136
137
bool QSvgPath::separateFillStroke(const QPainter *p, const QSvgExtraStates &s) const
138
159k
{
139
159k
    return !qFuzzyCompare(s.fillOpacity, s.strokeOpacity)
140
32.5k
            || qFuzzyIsNull(p->pen().widthF());
141
159k
}
142
143
QRectF QSvgPath::internalFastBounds(QPainter *p, QSvgExtraStates &) const
144
78.5k
{
145
78.5k
    return p->transform().mapRect(m_path.controlPointRect());
146
78.5k
}
147
148
QRectF QSvgPath::internalBounds(QPainter *p, QSvgExtraStates &) const
149
41.7k
{
150
41.7k
    qreal sw = strokeWidth(p);
151
41.7k
    return qFuzzyIsNull(sw) ? p->transform().map(m_path).boundingRect()
152
41.7k
                            : boundsOnStroke(p, m_path, sw, BoundsMode::Simplistic);
153
41.7k
}
154
155
QRectF QSvgPath::decoratedInternalBounds(QPainter *p, QSvgExtraStates &s) const
156
0
{
157
0
    qreal sw = strokeWidth(p);
158
0
    QRectF rect = qFuzzyIsNull(sw) ? p->transform().map(m_path).boundingRect()
159
0
                                   : boundsOnStroke(p, m_path, sw, BoundsMode::IncludeMiterLimit);
160
0
    rect |= QSvgMarker::markersBoundsForNode(this, p, s);
161
0
    return filterRegion(rect);
162
0
}
163
164
bool QSvgPath::requiresGroupRendering() const
165
135
{
166
135
    return hasAnyMarker();
167
135
}
168
169
QSvgPolygon::QSvgPolygon(QSvgNode *parent, const QPolygonF &poly)
170
0
    : QSvgNode(parent), m_poly(poly)
171
0
{
172
0
}
173
174
QRectF QSvgPolygon::internalFastBounds(QPainter *p, QSvgExtraStates &) const
175
0
{
176
0
    return p->transform().mapRect(m_poly.boundingRect());
177
0
}
178
179
QRectF QSvgPolygon::internalBounds(QPainter *p, QSvgExtraStates &s) const
180
0
{
181
0
    return internalBounds(p, s, BoundsMode::Simplistic);
182
0
}
183
184
QRectF QSvgPolygon::decoratedInternalBounds(QPainter *p, QSvgExtraStates &s) const
185
0
{
186
0
    QRectF rect = internalBounds(p, s, BoundsMode::IncludeMiterLimit);
187
0
    rect |= QSvgMarker::markersBoundsForNode(this, p, s);
188
0
    return filterRegion(rect);
189
0
}
190
191
bool QSvgPolygon::requiresGroupRendering() const
192
0
{
193
0
    return hasAnyMarker();
194
0
}
195
196
QRectF QSvgPolygon::internalBounds(QPainter *p, QSvgExtraStates &, BoundsMode mode) const
197
0
{
198
0
    qreal sw = strokeWidth(p);
199
0
    if (qFuzzyIsNull(sw)) {
200
0
        return p->transform().map(m_poly).boundingRect();
201
0
    } else {
202
0
        QPainterPath path;
203
0
        path.addPolygon(m_poly);
204
0
        return boundsOnStroke(p, path, sw, mode);
205
0
    }
206
0
}
207
208
void QSvgPolygon::drawCommand(QPainter *p, QSvgExtraStates &states)
209
0
{
210
0
    if (m_poly.boundingRect().isNull() && p->pen().capStyle() != Qt::FlatCap)
211
0
        p->drawPoint(m_poly.first());
212
0
    else
213
0
        p->drawPolygon(m_poly, states.fillRule);
214
0
    QSvgMarker::drawMarkersForNode(this, p, states);
215
0
}
216
217
bool QSvgPolygon::separateFillStroke(const QPainter *, const QSvgExtraStates &) const
218
0
{
219
0
    return true;
220
0
}
221
222
QSvgPolyline::QSvgPolyline(QSvgNode *parent, const QPolygonF &poly)
223
0
    : QSvgNode(parent), m_poly(poly)
224
0
{
225
226
0
}
227
228
void QSvgPolyline::drawCommand(QPainter *p, QSvgExtraStates &states)
229
0
{
230
0
    if (p->brush().style() != Qt::NoBrush) {
231
0
        p->drawPolygon(m_poly, states.fillRule);
232
0
    } else {
233
0
        if (m_poly.boundingRect().isNull() && p->pen().capStyle() != Qt::FlatCap)
234
0
            p->drawPoint(m_poly.first());
235
0
        else
236
0
            p->drawPolyline(m_poly);
237
0
        QSvgMarker::drawMarkersForNode(this, p, states);
238
0
    }
239
0
}
240
241
bool QSvgPolyline::separateFillStroke(const QPainter *, const QSvgExtraStates &) const
242
0
{
243
0
    return true;
244
0
}
245
246
QSvgRect::QSvgRect(QSvgNode *node, const QRectF &rect, qreal rx, qreal ry)
247
0
    : QSvgNode(node),
248
0
      m_rect(rect), m_rx(rx), m_ry(ry)
249
0
{
250
0
}
251
252
QRectF QSvgRect::internalFastBounds(QPainter *p, QSvgExtraStates &) const
253
0
{
254
0
    return p->transform().mapRect(m_rect);
255
0
}
256
257
QRectF QSvgRect::internalBounds(QPainter *p, QSvgExtraStates &s) const
258
0
{
259
0
    return internalBounds(p, s, BoundsMode::Simplistic);
260
0
}
261
262
QRectF QSvgRect::decoratedInternalBounds(QPainter *p, QSvgExtraStates &s) const
263
0
{
264
0
    return filterRegion(internalBounds(p, s, BoundsMode::IncludeMiterLimit));
265
0
}
266
267
QRectF QSvgRect::internalBounds(QPainter *p, QSvgExtraStates &, BoundsMode mode) const
268
0
{
269
0
    qreal sw = strokeWidth(p);
270
0
    if (qFuzzyIsNull(sw)) {
271
0
        return p->transform().mapRect(m_rect);
272
0
    } else {
273
0
        QPainterPath path;
274
0
        path.addRect(m_rect);
275
0
        return boundsOnStroke(p, path, sw, mode);
276
0
    }
277
0
}
278
279
void QSvgRect::drawCommand(QPainter *p, QSvgExtraStates &)
280
0
{
281
0
    if (m_rx || m_ry)
282
0
        p->drawRoundedRect(m_rect, m_rx, m_ry, Qt::RelativeSize);
283
0
    else
284
0
        p->drawRect(m_rect);
285
0
}
286
287
bool QSvgRect::separateFillStroke(const QPainter *, const QSvgExtraStates &) const
288
0
{
289
0
    return true;
290
0
}
291
292
QSvgTspan * const QSvgText::LINEBREAK = 0;
293
294
QSvgText::QSvgText(QSvgNode *parent, const QPointF &coord)
295
0
    : QSvgNode(parent)
296
0
    , m_coord(coord)
297
0
    , m_size(0, 0)
298
0
    , m_type(Text)
299
0
    , m_mode(Default)
300
0
{
301
0
}
302
303
QSvgText::~QSvgText()
304
0
{
305
0
    for (int i = 0; i < m_tspans.size(); ++i) {
306
0
        if (m_tspans[i] != LINEBREAK)
307
0
            delete m_tspans[i];
308
0
    }
309
0
}
310
311
void QSvgText::setTextArea(const QSizeF &size)
312
0
{
313
0
    m_size = size;
314
0
    m_type = Textarea;
315
0
}
316
317
QRectF QSvgText::internalFastBounds(QPainter *p, QSvgExtraStates &) const
318
0
{
319
0
    QFont font = m_style.font ? m_style.font->qfont() : p->font();
320
0
    QFontMetricsF fm(font);
321
322
0
    int charCount = 0;
323
0
    for (int i = 0; i < m_tspans.size(); ++i) {
324
0
        if (m_tspans.at(i) != LINEBREAK)
325
0
            charCount += m_tspans.at(i)->text().size();
326
0
    }
327
328
0
    QRectF approxMaximumBrect(m_coord.x(),
329
0
                              m_coord.y(),
330
0
                              charCount * fm.averageCharWidth(),
331
0
                              -m_tspans.size() * fm.height());
332
0
    return p->transform().mapRect(approxMaximumBrect);
333
0
}
334
335
QRectF QSvgText::internalBounds(QPainter *p, QSvgExtraStates &states) const
336
0
{
337
0
    QRectF boundingRect;
338
0
    if (shouldDrawNode(p, states))
339
0
        draw_helper(p, states, &boundingRect);
340
0
    return p->transform().mapRect(boundingRect);
341
0
}
342
343
void QSvgText::drawCommand(QPainter *p, QSvgExtraStates &states)
344
0
{
345
0
    draw_helper(p, states);
346
0
}
347
348
bool QSvgText::shouldDrawNode(QPainter *p, QSvgExtraStates &) const
349
0
{
350
0
    qsizetype numChars = 0;
351
0
    qreal originalFontSize = p->font().pointSizeF();
352
0
    qreal maxFontSize = originalFontSize;
353
0
    for (const QSvgTspan *span : std::as_const(m_tspans)) {
354
0
        if (span == LINEBREAK)
355
0
            continue;
356
357
0
        numChars += span->text().size();
358
359
0
        QSvgFontStyle *style = static_cast<QSvgFontStyle *>(span->styleProperty(QSvgStyleProperty::FONT));
360
0
        if (style != nullptr && style->qfont().pointSizeF() > maxFontSize)
361
0
            maxFontSize = style->qfont().pointSizeF();
362
0
    }
363
364
0
    QFont font = p->font();
365
0
    font.setPixelSize(maxFontSize);
366
0
    QFontMetricsF fm(font);
367
0
    if (m_tspans.size() * fm.height() >= QT_SVG_MAX_LAYOUT_SIZE) {
368
0
        qCWarning(lcSvgDraw) << "Text element too high to lay out, ignoring";
369
0
        return false;
370
0
    }
371
372
0
    if (numChars * fm.maxWidth() >= QT_SVG_MAX_LAYOUT_SIZE) {
373
0
        qCWarning(lcSvgDraw) << "Text element too wide to lay out, ignoring";
374
0
        return false;
375
0
    }
376
377
0
    return true;
378
0
}
379
380
bool QSvgText::separateFillStroke(const QPainter *, const QSvgExtraStates &) const
381
0
{
382
0
    return true;
383
0
}
384
385
void QSvgText::draw_helper(QPainter *p, QSvgExtraStates &states, QRectF *boundingRect) const
386
0
{
387
0
    const bool isPainting = (boundingRect == nullptr);
388
0
    if (!isPainting || shouldDrawNode(p, states)) {
389
0
        QFont font = p->font();
390
0
        Qt::Alignment alignment = states.textAnchor;
391
392
0
        qreal y = 0;
393
0
        bool initial = true;
394
0
        qreal px = m_coord.x();
395
0
        qreal py = m_coord.y();
396
397
0
        if (m_type == Textarea) {
398
0
            if (alignment == Qt::AlignHCenter)
399
0
                px += m_size.width() / 2;
400
0
            else if (alignment == Qt::AlignRight)
401
0
                px += m_size.width();
402
0
        }
403
404
0
        QRectF bounds;
405
0
        if (m_size.height() != 0)
406
0
            bounds = QRectF(0, py, 1, m_size.height()); // x and width are not used.
407
408
0
        bool appendSpace = false;
409
0
        QStringList paragraphs;
410
0
        QList<QList<QTextLayout::FormatRange> > formatRanges(1);
411
0
        paragraphs.push_back(QString());
412
413
0
        for (int i = 0; i < m_tspans.size(); ++i) {
414
0
            if (m_tspans[i] == LINEBREAK) {
415
0
                if (m_type == Textarea) {
416
0
                    if (paragraphs.back().isEmpty()) {
417
0
                        font.setPixelSize(font.pointSizeF());
418
0
                        font.setHintingPreference(QFont::PreferNoHinting);
419
420
0
                        QTextLayout::FormatRange range;
421
0
                        range.start = 0;
422
0
                        range.length = 1;
423
0
                        range.format.setFont(font);
424
0
                        formatRanges.back().append(range);
425
426
0
                        paragraphs.back().append(QLatin1Char(' '));;
427
0
                    }
428
0
                    appendSpace = false;
429
0
                    paragraphs.push_back(QString());
430
0
                    formatRanges.resize(formatRanges.size() + 1);
431
0
                }
432
0
            } else {
433
0
                WhitespaceMode mode = m_tspans[i]->whitespaceMode();
434
0
                m_tspans[i]->applyStyle(p, states);
435
436
0
                font = p->font();
437
0
                font.setPixelSize(font.pointSizeF());
438
0
                font.setHintingPreference(QFont::PreferNoHinting);
439
440
0
                QString newText(m_tspans[i]->text());
441
0
                newText.replace(QLatin1Char('\t'), QLatin1Char(' '));
442
0
                newText.replace(QLatin1Char('\n'), QLatin1Char(' '));
443
444
0
                bool prependSpace = !appendSpace && !m_tspans[i]->isTspan() && (mode == Default) && !paragraphs.back().isEmpty() && newText.startsWith(QLatin1Char(' '));
445
0
                if (appendSpace || prependSpace)
446
0
                    paragraphs.back().append(QLatin1Char(' '));
447
448
0
                bool appendSpaceNext = (!m_tspans[i]->isTspan() && (mode == Default) && newText.endsWith(QLatin1Char(' ')));
449
450
0
                if (mode == Default) {
451
0
                    newText = newText.simplified();
452
0
                    if (newText.isEmpty())
453
0
                        appendSpaceNext = false;
454
0
                }
455
456
0
                QTextLayout::FormatRange range;
457
0
                range.start = paragraphs.back().size();
458
0
                range.length = newText.size();
459
0
                range.format.setFont(font);
460
0
                if (p->pen().style() != Qt::NoPen && p->pen().brush() != Qt::NoBrush)
461
0
                    range.format.setTextOutline(p->pen());
462
                // work around QTBUG-136696: NoBrush fills the foreground with the outline's pen
463
0
                range.format.setForeground(p->brush().style() == Qt::NoBrush
464
0
                                           ? QColor(Qt::transparent) : p->brush());
465
466
0
                if (appendSpace) {
467
0
                    Q_ASSERT(!formatRanges.back().isEmpty());
468
0
                    ++formatRanges.back().back().length;
469
0
                } else if (prependSpace) {
470
0
                    --range.start;
471
0
                    ++range.length;
472
0
                }
473
0
                formatRanges.back().append(range);
474
475
0
                appendSpace = appendSpaceNext;
476
0
                paragraphs.back() += newText;
477
478
0
                m_tspans[i]->revertStyle(p, states);
479
0
            }
480
0
        }
481
482
0
        if (states.svgFont) {
483
            // SVG fonts not fully supported...
484
0
            if (!m_glyphsToDraw)
485
0
                m_glyphsToDraw = states.svgFont->toGlyphs(paragraphs.join(QLatin1Char('\n')));
486
0
            if (isPainting) {
487
0
                states.svgFont->draw(p, m_coord, m_glyphsToDraw.value(),
488
0
                                     p->font().pointSizeF(), states.textAnchor);
489
0
            } else {
490
0
                *boundingRect = states.svgFont->boundingRect(p, m_coord, m_glyphsToDraw.value(),
491
0
                                                             p->font().pointSizeF(), states.textAnchor);
492
0
            }
493
0
        } else {
494
0
            QRectF brect;
495
0
            for (int i = 0; i < paragraphs.size(); ++i) {
496
0
                QTextLayout tl(paragraphs[i]);
497
0
                QTextOption op = tl.textOption();
498
0
                op.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
499
0
                tl.setTextOption(op);
500
0
                tl.setFormats(formatRanges[i]);
501
0
                tl.beginLayout();
502
503
0
                forever {
504
0
                    QTextLine line = tl.createLine();
505
0
                    if (!line.isValid())
506
0
                        break;
507
0
                    if (m_size.width() != 0)
508
0
                        line.setLineWidth(m_size.width());
509
0
                }
510
0
                tl.endLayout();
511
512
0
                bool endOfBoundsReached = false;
513
0
                for (int i = 0; i < tl.lineCount(); ++i) {
514
0
                    QTextLine line = tl.lineAt(i);
515
516
0
                    qreal x = 0;
517
0
                    if (alignment == Qt::AlignHCenter)
518
0
                        x -= 0.5 * line.naturalTextWidth();
519
0
                    else if (alignment == Qt::AlignRight)
520
0
                        x -= line.naturalTextWidth();
521
522
0
                    if (initial && m_type == Text)
523
0
                        y -= line.ascent();
524
0
                    initial = false;
525
526
0
                    line.setPosition(QPointF(x, y));
527
0
                    brect |= line.naturalTextRect();
528
529
                    // Check if the current line fits into the bounding rectangle.
530
0
                    if ((m_size.width() != 0 && line.naturalTextWidth() > m_size.width())
531
0
                        || (m_size.height() != 0 && y + line.height() > m_size.height())) {
532
                        // I need to set the bounds height to 'y-epsilon' to avoid drawing the current
533
                        // line. Since the font is scaled to 100 units, 1 should be a safe epsilon.
534
0
                        bounds.setHeight(y - 1);
535
0
                        endOfBoundsReached = true;
536
0
                        break;
537
0
                    }
538
539
0
                    y += 1.1 * line.height();
540
0
                }
541
0
                if (isPainting)
542
0
                    tl.draw(p, QPointF(px, py), QList<QTextLayout::FormatRange>(), bounds);
543
544
0
                if (endOfBoundsReached)
545
0
                    break;
546
0
            }
547
0
            if (boundingRect) {
548
0
                brect.translate(m_coord);
549
0
                if (bounds.height() > 0)
550
0
                    brect.setBottom(qMin(brect.bottom(), bounds.bottom()));
551
0
                *boundingRect = brect;
552
0
            }
553
0
        }
554
0
    }
555
0
}
556
557
void QSvgText::addText(QStringView text)
558
0
{
559
0
    m_tspans.append(new QSvgTspan(this, false));
560
0
    m_tspans.back()->setWhitespaceMode(m_mode);
561
0
    m_tspans.back()->addText(text);
562
0
}
563
564
QSvgUse::QSvgUse(const QPointF &start, QSvgNode *parent, QSvgNode *node)
565
7.82k
    : QSvgNode(parent), m_link(node), m_start(start), m_recursing(false)
566
7.82k
{
567
568
7.82k
}
569
570
void QSvgUse::drawCommand(QPainter *p, QSvgExtraStates &states)
571
0
{
572
0
    if (Q_UNLIKELY(!m_link || isDescendantOf(m_link) || m_recursing))
573
0
        return;
574
575
0
    Q_ASSERT(states.nestedUseCount == 0 || states.nestedUseLevel > 0);
576
0
    if (states.nestedUseLevel > 3 && states.nestedUseCount > (256 + states.nestedUseLevel * 2)) {
577
0
        qCDebug(lcSvgDraw, "Too many nested use nodes at #%s!", qPrintable(m_linkId));
578
0
        return;
579
0
    }
580
581
0
    QScopedValueRollback<bool> inUseGuard(states.inUse, true);
582
583
0
    if (!m_start.isNull()) {
584
0
        p->translate(m_start);
585
0
    }
586
0
    if (states.nestedUseLevel > 0)
587
0
        ++states.nestedUseCount;
588
0
    {
589
0
        QScopedValueRollback<int> useLevelGuard(states.nestedUseLevel, states.nestedUseLevel + 1);
590
0
        QScopedValueRollback<bool> recursingGuard(m_recursing, true);
591
0
        m_link->draw(p, states);
592
0
    }
593
0
    if (states.nestedUseLevel == 0)
594
0
        states.nestedUseCount = 0;
595
596
0
    if (!m_start.isNull()) {
597
0
        p->translate(-m_start);
598
0
    }
599
0
}
600
601
QSvgNode::Type QSvgDummyNode::type() const
602
0
{
603
0
    return FeUnsupported;
604
0
}
605
606
QSvgNode::Type QSvgCircle::type() const
607
0
{
608
0
    return Circle;
609
0
}
610
611
QSvgNode::Type QSvgEllipse::type() const
612
0
{
613
0
    return Ellipse;
614
0
}
615
616
QSvgNode::Type QSvgImage::type() const
617
0
{
618
0
    return Image;
619
0
}
620
621
QSvgNode::Type QSvgLine::type() const
622
0
{
623
0
    return Line;
624
0
}
625
626
QSvgNode::Type QSvgPath::type() const
627
1.92M
{
628
1.92M
    return Path;
629
1.92M
}
630
631
QSvgNode::Type QSvgPolygon::type() const
632
0
{
633
0
    return Polygon;
634
0
}
635
636
QSvgNode::Type QSvgPolyline::type() const
637
0
{
638
0
    return Polyline;
639
0
}
640
641
QSvgNode::Type QSvgRect::type() const
642
0
{
643
0
    return Rect;
644
0
}
645
646
QSvgNode::Type QSvgText::type() const
647
0
{
648
0
    return m_type;
649
0
}
650
651
QSvgNode::Type QSvgUse::type() const
652
121k
{
653
121k
    return Use;
654
121k
}
655
656
QSvgNode::Type QSvgVideo::type() const
657
0
{
658
0
    return Video;
659
0
}
660
661
QRectF QSvgUse::internalBounds(QPainter *p, QSvgExtraStates &states) const
662
78
{
663
78
    QRectF bounds;
664
78
    if (Q_LIKELY(m_link && !isDescendantOf(m_link) && !m_recursing)) {
665
0
        QScopedValueRollback<bool> guard(m_recursing, true);
666
0
        p->translate(m_start);
667
0
        bounds = m_link->bounds(p, states);
668
0
        p->translate(-m_start);
669
0
    }
670
78
    return bounds;
671
78
}
672
673
QRectF QSvgUse::decoratedInternalBounds(QPainter *p, QSvgExtraStates &states) const
674
0
{
675
0
    QRectF bounds;
676
0
    if (Q_LIKELY(m_link && !isDescendantOf(m_link) && !m_recursing)) {
677
0
        QScopedValueRollback<bool> guard(m_recursing, true);
678
0
        p->translate(m_start);
679
0
        bounds = m_link->decoratedBounds(p, states);
680
0
        p->translate(-m_start);
681
0
    }
682
0
    return bounds;
683
0
}
684
685
QRectF QSvgPolyline::internalFastBounds(QPainter *p, QSvgExtraStates &) const
686
0
{
687
0
    return p->transform().mapRect(m_poly.boundingRect());
688
0
}
689
690
QRectF QSvgPolyline::internalBounds(QPainter *p, QSvgExtraStates &s) const
691
0
{
692
0
    return internalBounds(p, s, BoundsMode::Simplistic);
693
0
}
694
695
QRectF QSvgPolyline::decoratedInternalBounds(QPainter *p, QSvgExtraStates &s) const
696
0
{
697
0
    QRectF rect = internalBounds(p, s, BoundsMode::IncludeMiterLimit);
698
0
    rect |= QSvgMarker::markersBoundsForNode(this, p, s);
699
0
    return filterRegion(rect);
700
0
}
701
702
bool QSvgPolyline::requiresGroupRendering() const
703
0
{
704
0
    return hasAnyMarker();
705
0
}
706
707
QRectF QSvgPolyline::internalBounds(QPainter *p, QSvgExtraStates &, BoundsMode mode) const
708
0
{
709
0
    qreal sw = strokeWidth(p);
710
0
    if (qFuzzyIsNull(sw)) {
711
0
        return p->transform().map(m_poly).boundingRect();
712
0
    } else {
713
0
        QPainterPath path;
714
0
        path.addPolygon(m_poly);
715
0
        return boundsOnStroke(p, path, sw, mode);
716
0
    }
717
0
}
718
719
QRectF QSvgImage::internalBounds(QPainter *p, QSvgExtraStates &) const
720
0
{
721
0
    return p->transform().mapRect(m_bounds);
722
0
}
723
724
QRectF QSvgLine::internalFastBounds(QPainter *p, QSvgExtraStates &) const
725
0
{
726
0
    QPointF p1 = p->transform().map(m_line.p1());
727
0
    QPointF p2 = p->transform().map(m_line.p2());
728
0
    qreal minX = qMin(p1.x(), p2.x());
729
0
    qreal minY = qMin(p1.y(), p2.y());
730
0
    qreal maxX = qMax(p1.x(), p2.x());
731
0
    qreal maxY = qMax(p1.y(), p2.y());
732
0
    return QRectF(minX, minY, maxX - minX, maxY - minY);
733
0
}
734
735
QRectF QSvgLine::internalBounds(QPainter *p, QSvgExtraStates &s) const
736
0
{
737
0
    return  internalBounds(p, s, BoundsMode::Simplistic);
738
0
}
739
740
QRectF QSvgLine::decoratedInternalBounds(QPainter *p, QSvgExtraStates &s) const
741
0
{
742
0
    QRectF rect = internalBounds(p, s, BoundsMode::IncludeMiterLimit);
743
0
    rect |= QSvgMarker::markersBoundsForNode(this, p, s);
744
0
    return filterRegion(rect);
745
0
}
746
747
bool QSvgLine::requiresGroupRendering() const
748
0
{
749
0
    return hasAnyMarker();
750
0
}
751
752
QRectF QSvgLine::internalBounds(QPainter *p, QSvgExtraStates &s, BoundsMode mode) const
753
0
{
754
0
    qreal sw = strokeWidth(p);
755
0
    if (qFuzzyIsNull(sw)) {
756
0
        return internalFastBounds(p, s);
757
0
    } else {
758
0
        QPainterPath path;
759
0
        path.moveTo(m_line.p1());
760
0
        path.lineTo(m_line.p2());
761
0
        return boundsOnStroke(p, path, sw, mode);
762
0
    }
763
0
}
764
765
QT_END_NAMESPACE