Coverage Report

Created: 2026-04-29 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtsvg/src/svg/qsvgrenderer.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 "qsvgrenderer.h"
7
8
#ifndef QT_NO_SVGRENDERER
9
10
#include "qsvgdocument_p.h"
11
12
#include "qbytearray.h"
13
#include "qtimer.h"
14
#include "qtransform.h"
15
#include "qdebug.h"
16
#include "private/qobject_p.h"
17
18
19
QT_BEGIN_NAMESPACE
20
21
/*!
22
    \class QSvgRenderer
23
    \inmodule QtSvg
24
    \ingroup painting
25
26
    \brief The QSvgRenderer class is used to draw the contents of SVG files onto paint devices.
27
    \since 4.1
28
    \reentrant
29
30
    Using QSvgRenderer, Scalable Vector Graphics (SVG) can be rendered onto any QPaintDevice
31
    subclass, including QWidget, QImage, and QGLWidget.
32
33
    QSvgRenderer provides an API that supports basic features of SVG rendering, such as loading
34
    and rendering of static drawings, and more interactive features like animation. Since the
35
    rendering is performed using QPainter, SVG drawings can be rendered on any subclass of
36
    QPaintDevice.
37
38
    SVG drawings are either loaded when an QSvgRenderer is constructed, or loaded later
39
    using the load() functions. Data is either supplied directly as serialized XML, or
40
    indirectly using a file name. If a valid file has been loaded, either when the renderer
41
    is constructed or at some later time, isValid() returns true; otherwise it returns false.
42
    QSvgRenderer provides the render() slot to render the current document, or the current
43
    frame of an animated document, using a given painter.
44
45
    The defaultSize() function provides information about the amount of space that is required
46
    to render the currently loaded SVG file. This is useful for paint devices, such as QWidget,
47
    that often need to supply a size hint to their parent layout.
48
    The default size of a drawing may differ from its visible area, found using the \l viewBox
49
    property.
50
51
    Animated SVG drawings are supported, and can be controlled with a simple collection of
52
    functions and properties:
53
54
    \list
55
    \li The animated() function indicates whether a drawing contains animation information.
56
    \omit
57
    \li The animationDuration() function provides the duration in milliseconds of the
58
       animation, without taking any looping into account.
59
    \li The \l currentFrame property contains the current frame of the animation.
60
    \endomit
61
    \li The \l framesPerSecond property contains the rate at which the animation plays.
62
    \endlist
63
64
    Finally, the QSvgRenderer class provides the repaintNeeded() signal which is emitted
65
    whenever the rendering of the document needs to be updated.
66
67
    \sa QSvgWidget, {Qt SVG C++ Classes}, QPicture
68
*/
69
70
class QSvgRendererPrivate : public QObjectPrivate
71
{
72
0
    Q_DECLARE_PUBLIC(QSvgRenderer)
73
0
public:
74
0
    explicit QSvgRendererPrivate()
75
42.1k
        : QObjectPrivate(),
76
42.1k
          timer(0),
77
42.1k
          fps(30)
78
42.1k
    {
79
42.1k
        options = defaultOptions();
80
42.1k
    }
81
82
    void startOrStopTimer()
83
42.1k
    {
84
42.1k
        if (animationEnabled && render && render->animated() && fps > 0) {
85
0
            ensureTimerCreated();
86
0
            timer->start(1000 / fps);
87
42.1k
        } else if (timer) {
88
0
            timer->stop();
89
0
        }
90
42.1k
    }
91
92
    void ensureTimerCreated()
93
0
    {
94
0
        Q_Q(QSvgRenderer);
95
0
        if (!timer) {
96
0
            timer = new QTimer(q);
97
0
            q->connect(timer, &QTimer::timeout, q, &QSvgRenderer::repaintNeeded);
98
0
        }
99
0
    }
100
101
    static void callRepaintNeeded(QSvgRenderer *const q);
102
103
    static QtSvg::Options defaultOptions()
104
42.1k
    {
105
42.1k
        static bool envOk = false;
106
42.1k
        static QtSvg::Options envOpts = QtSvg::Options::fromInt(
107
42.1k
                qEnvironmentVariableIntValue("QT_SVG_DEFAULT_OPTIONS", &envOk));
108
42.1k
        return envOk ? envOpts : appDefaultOptions;
109
42.1k
    }
110
111
    std::unique_ptr<QSvgDocument> render;
112
    QTimer *timer;
113
    int fps;
114
    QtSvg::Options options;
115
    static QtSvg::Options appDefaultOptions;
116
    bool animationEnabled = true;
117
};
118
119
QtSvg::Options QSvgRendererPrivate::appDefaultOptions;
120
121
/*!
122
    Constructs a new renderer with the given \a parent.
123
*/
124
QSvgRenderer::QSvgRenderer(QObject *parent)
125
32.4k
    : QObject(*(new QSvgRendererPrivate), parent)
126
32.4k
{
127
32.4k
}
128
129
/*!
130
    Constructs a new renderer with the given \a parent and loads the contents of the
131
    SVG file with the specified \a filename.
132
*/
133
QSvgRenderer::QSvgRenderer(const QString &filename, QObject *parent)
134
9.67k
    : QObject(*new QSvgRendererPrivate, parent)
135
9.67k
{
136
9.67k
    load(filename);
137
9.67k
}
138
139
/*!
140
    Constructs a new renderer with the given \a parent and loads the SVG data
141
    from the byte array specified by \a contents.
142
*/
143
QSvgRenderer::QSvgRenderer(const QByteArray &contents, QObject *parent)
144
0
    : QObject(*new QSvgRendererPrivate, parent)
145
0
{
146
0
    load(contents);
147
0
}
148
149
/*!
150
    \since 4.5
151
152
    Constructs a new renderer with the given \a parent and loads the SVG data
153
    using the stream reader specified by \a contents.
154
*/
155
QSvgRenderer::QSvgRenderer(QXmlStreamReader *contents, QObject *parent)
156
0
    : QObject(*new QSvgRendererPrivate, parent)
157
0
{
158
0
    load(contents);
159
0
}
160
161
/*!
162
    Destroys the renderer.
163
*/
164
QSvgRenderer::~QSvgRenderer()
165
42.1k
{
166
167
42.1k
}
168
169
/*!
170
    Returns true if there is a valid current document; otherwise returns false.
171
*/
172
bool QSvgRenderer::isValid() const
173
9.67k
{
174
9.67k
    Q_D(const QSvgRenderer);
175
9.67k
    return bool(d->render);
176
9.67k
}
177
178
/*!
179
    Returns the default size of the document contents.
180
*/
181
QSize QSvgRenderer::defaultSize() const
182
2.09k
{
183
2.09k
    Q_D(const QSvgRenderer);
184
2.09k
    if (d->render)
185
2.09k
        return d->render->size();
186
0
    else
187
0
        return QSize();
188
2.09k
}
189
190
/*!
191
    Returns viewBoxF().toRect().
192
193
    \sa viewBoxF()
194
*/
195
QRect QSvgRenderer::viewBox() const
196
0
{
197
0
    Q_D(const QSvgRenderer);
198
0
    if (d->render)
199
0
        return d->render->viewBox().toRect();
200
0
    else
201
0
        return QRect();
202
0
}
203
204
/*!
205
    \property QSvgRenderer::viewBox
206
    \brief the rectangle specifying the visible area of the document in logical coordinates
207
    \since 4.2
208
*/
209
void QSvgRenderer::setViewBox(const QRect &viewbox)
210
0
{
211
0
    Q_D(QSvgRenderer);
212
0
    if (d->render)
213
0
        d->render->setViewBox(viewbox);
214
0
}
215
216
/*!
217
    Returns true if the current document contains animated elements; otherwise
218
    returns false.
219
220
    \sa framesPerSecond()
221
*/
222
bool QSvgRenderer::animated() const
223
285
{
224
285
    Q_D(const QSvgRenderer);
225
285
    if (d->render)
226
285
        return d->render->animated();
227
0
    else
228
0
        return false;
229
285
}
230
231
/*!
232
    \property QSvgRenderer::animationEnabled
233
    \brief whether the animation should run, if the SVG is animated
234
235
    Setting the property to false stops the animation timer.
236
    Setting the property to true starts the animation timer,
237
    provided that the SVG contains animated elements.
238
239
    If the SVG is not animated, the property will have no effect.
240
    Otherwise, the property defaults to true.
241
242
    \sa animated()
243
244
    \since 6.7
245
*/
246
bool QSvgRenderer::isAnimationEnabled() const
247
0
{
248
0
    Q_D(const QSvgRenderer);
249
0
    return d->animationEnabled;
250
0
}
251
252
void QSvgRenderer::setAnimationEnabled(bool enable)
253
0
{
254
0
    Q_D(QSvgRenderer);
255
0
    d->animationEnabled = enable;
256
0
    d->startOrStopTimer();
257
0
}
258
259
/*!
260
    \property QSvgRenderer::framesPerSecond
261
    \brief the number of frames per second to be shown
262
263
    The number of frames per second is 0 if the current document is not animated.
264
265
    \sa animated()
266
*/
267
int QSvgRenderer::framesPerSecond() const
268
0
{
269
0
    Q_D(const QSvgRenderer);
270
0
    return d->fps;
271
0
}
272
273
void QSvgRenderer::setFramesPerSecond(int num)
274
0
{
275
0
    Q_D(QSvgRenderer);
276
0
    if (num < 0) {
277
0
        qWarning("QSvgRenderer::setFramesPerSecond: Cannot set negative value %d", num);
278
0
        return;
279
0
    }
280
0
    d->fps = num;
281
0
    d->startOrStopTimer();
282
0
}
283
284
/*!
285
    \property QSvgRenderer::aspectRatioMode
286
287
    \brief how rendering adheres to the SVG view box aspect ratio
288
289
    The accepted modes are:
290
    \list
291
    \li Qt::IgnoreAspectRatio (the default): the aspect ratio is ignored and the
292
        rendering is stretched to the target bounds.
293
    \li Qt::KeepAspectRatio: rendering is centered and scaled as large as possible
294
        within the target bounds while preserving aspect ratio.
295
    \endlist
296
297
    \since 5.15
298
*/
299
300
Qt::AspectRatioMode QSvgRenderer::aspectRatioMode() const
301
0
{
302
0
    Q_D(const QSvgRenderer);
303
0
    if (d->render && d->render->preserveAspectRatio())
304
0
        return Qt::KeepAspectRatio;
305
0
    return Qt::IgnoreAspectRatio;
306
0
}
307
308
void QSvgRenderer::setAspectRatioMode(Qt::AspectRatioMode mode)
309
0
{
310
0
    Q_D(QSvgRenderer);
311
0
    if (d->render) {
312
0
        if (mode == Qt::KeepAspectRatio)
313
0
            d->render->setPreserveAspectRatio(true);
314
0
        else if (mode == Qt::IgnoreAspectRatio)
315
0
            d->render->setPreserveAspectRatio(false);
316
0
    }
317
0
}
318
319
/*!
320
    \property QSvgRenderer::options
321
    \since 6.7
322
323
    This property holds a set of QtSvg::Option flags that can be used
324
    to enable or disable various features of the parsing and rendering of SVG files.
325
326
    In order to take effect, this property must be set \c before load() is executed. Note that the
327
    constructors taking an SVG source parameter will perform loading during construction.
328
329
    \sa setDefaultOptions
330
 */
331
QtSvg::Options QSvgRenderer::options() const
332
0
{
333
0
    Q_D(const QSvgRenderer);
334
0
    return d->options;
335
0
}
336
337
void QSvgRenderer::setOptions(QtSvg::Options flags)
338
0
{
339
0
    Q_D(QSvgRenderer);
340
0
    d->options = flags;
341
0
}
342
343
/*!
344
    Sets the option flags that renderers will be created with to \a flags.
345
    By default, no flags are set.
346
347
    At runtime, this can be overridden by the QT_SVG_DEFAULT_OPTIONS environment variable.
348
349
    \since 6.8
350
*/
351
352
void QSvgRenderer::setDefaultOptions(QtSvg::Options flags)
353
0
{
354
0
    QSvgRendererPrivate::appDefaultOptions = flags;
355
0
}
356
357
/*!
358
  \property QSvgRenderer::currentFrame
359
  \brief the current frame of the document's animation, or 0 if the document is not animated
360
  \internal
361
362
  \sa animationDuration(), framesPerSecond, animated()
363
*/
364
365
/*!
366
  \internal
367
*/
368
int QSvgRenderer::currentFrame() const
369
0
{
370
0
    Q_D(const QSvgRenderer);
371
0
    return d->render->currentFrame();
372
0
}
373
374
/*!
375
  \internal
376
*/
377
void QSvgRenderer::setCurrentFrame(int frame)
378
0
{
379
0
    Q_D(QSvgRenderer);
380
0
    d->render->setCurrentFrame(frame);
381
0
}
382
383
/*!
384
    \internal
385
386
    Returns the number of frames in the animation, or 0 if the current document is not
387
    animated.
388
389
    \sa animated(), framesPerSecond
390
*/
391
int QSvgRenderer::animationDuration() const
392
0
{
393
0
    Q_D(const QSvgRenderer);
394
0
    return d->render->animationDuration();
395
0
}
396
397
/*!
398
 \internal
399
 \since 4.5
400
401
 We can't have template functions, that's loadDocument(), as friends, for this
402
 code, so we let this function be a friend of QSvgRenderer instead.
403
 */
404
void QSvgRendererPrivate::callRepaintNeeded(QSvgRenderer *const q)
405
42.1k
{
406
42.1k
    emit q->repaintNeeded();
407
42.1k
}
408
409
template<typename TInputType>
410
static bool loadDocument(QSvgRenderer *const q,
411
                         QSvgRendererPrivate *const d,
412
                         const TInputType &in)
413
42.1k
{
414
42.1k
    d->render = QSvgDocument::load(in, d->options);
415
42.1k
    if (d->render && !d->render->size().isValid())
416
0
        d->render.reset();
417
418
42.1k
    d->startOrStopTimer();
419
420
42.1k
    if (d->render)
421
2.09k
        d->render->restartAnimation();
422
423
    //force first update
424
42.1k
    QSvgRendererPrivate::callRepaintNeeded(q);
425
426
42.1k
    return bool(d->render);
427
42.1k
}
qsvgrenderer.cpp:bool loadDocument<QString>(QSvgRenderer*, QSvgRendererPrivate*, QString const&)
Line
Count
Source
413
9.67k
{
414
9.67k
    d->render = QSvgDocument::load(in, d->options);
415
9.67k
    if (d->render && !d->render->size().isValid())
416
0
        d->render.reset();
417
418
9.67k
    d->startOrStopTimer();
419
420
9.67k
    if (d->render)
421
1.99k
        d->render->restartAnimation();
422
423
    //force first update
424
9.67k
    QSvgRendererPrivate::callRepaintNeeded(q);
425
426
9.67k
    return bool(d->render);
427
9.67k
}
qsvgrenderer.cpp:bool loadDocument<QByteArray>(QSvgRenderer*, QSvgRendererPrivate*, QByteArray const&)
Line
Count
Source
413
9.79k
{
414
9.79k
    d->render = QSvgDocument::load(in, d->options);
415
9.79k
    if (d->render && !d->render->size().isValid())
416
0
        d->render.reset();
417
418
9.79k
    d->startOrStopTimer();
419
420
9.79k
    if (d->render)
421
19
        d->render->restartAnimation();
422
423
    //force first update
424
9.79k
    QSvgRendererPrivate::callRepaintNeeded(q);
425
426
9.79k
    return bool(d->render);
427
9.79k
}
qsvgrenderer.cpp:bool loadDocument<QXmlStreamReader*>(QSvgRenderer*, QSvgRendererPrivate*, QXmlStreamReader* const&)
Line
Count
Source
413
22.7k
{
414
22.7k
    d->render = QSvgDocument::load(in, d->options);
415
22.7k
    if (d->render && !d->render->size().isValid())
416
0
        d->render.reset();
417
418
22.7k
    d->startOrStopTimer();
419
420
22.7k
    if (d->render)
421
76
        d->render->restartAnimation();
422
423
    //force first update
424
22.7k
    QSvgRendererPrivate::callRepaintNeeded(q);
425
426
22.7k
    return bool(d->render);
427
22.7k
}
428
429
/*!
430
    Loads the SVG file specified by \a filename, returning true if the content
431
    was successfully parsed; otherwise returns false.
432
*/
433
bool QSvgRenderer::load(const QString &filename)
434
9.67k
{
435
9.67k
    Q_D(QSvgRenderer);
436
9.67k
    return loadDocument(this, d, filename);
437
9.67k
}
438
439
/*!
440
    Loads the specified SVG format \a contents, returning true if the content
441
    was successfully parsed; otherwise returns false.
442
*/
443
bool QSvgRenderer::load(const QByteArray &contents)
444
9.79k
{
445
9.79k
    Q_D(QSvgRenderer);
446
9.79k
    return loadDocument(this, d, contents);
447
9.79k
}
448
449
/*!
450
  Loads the specified SVG in \a contents, returning true if the content
451
  was successfully parsed; otherwise returns false.
452
453
  The reader will be used from where it currently is positioned. If \a contents
454
  is \c null, behavior is undefined.
455
456
  \since 4.5
457
*/
458
bool QSvgRenderer::load(QXmlStreamReader *contents)
459
22.7k
{
460
22.7k
    Q_D(QSvgRenderer);
461
22.7k
    return loadDocument(this, d, contents);
462
22.7k
}
463
464
/*!
465
    Renders the current document, or the current frame of an animated
466
    document, using the given \a painter.
467
*/
468
void QSvgRenderer::render(QPainter *painter)
469
0
{
470
0
    Q_D(QSvgRenderer);
471
0
    if (d->render) {
472
0
        if (d->render->animator())
473
0
            d->render->animator()->advanceAnimations();
474
0
        d->render->draw(painter);
475
0
    }
476
0
}
477
478
/*!
479
    \fn void QSvgRenderer::repaintNeeded()
480
481
    This signal is emitted whenever the rendering of the document
482
    needs to be updated, usually for the purposes of animation.
483
*/
484
485
/*!
486
    Renders the given element with \a elementId using the given \a painter
487
    on the specified \a bounds. If the bounding rectangle is not specified
488
    the SVG element is mapped to the whole paint device.
489
*/
490
void QSvgRenderer::render(QPainter *painter, const QString &elementId,
491
                          const QRectF &bounds)
492
0
{
493
0
    Q_D(QSvgRenderer);
494
0
    if (d->render) {
495
0
        if (d->render->animator())
496
0
            d->render->animator()->advanceAnimations();
497
0
        d->render->draw(painter, elementId, bounds);
498
0
    }
499
0
}
500
501
/*!
502
    Renders the current document, or the current frame of an animated document,
503
    using the given \a painter on the specified \a bounds within the painter.
504
    If \a bounds is not empty, the output will be scaled to fill it, ignoring
505
    any aspect ratio implied by the SVG.
506
*/
507
void QSvgRenderer::render(QPainter *painter, const QRectF &bounds)
508
1.99k
{
509
1.99k
    Q_D(QSvgRenderer);
510
1.99k
    if (d->render) {
511
1.99k
        if (d->render->animator())
512
1.99k
            d->render->animator()->advanceAnimations();
513
1.99k
        d->render->draw(painter, bounds);
514
1.99k
    }
515
1.99k
}
516
517
QRectF QSvgRenderer::viewBoxF() const
518
0
{
519
0
    Q_D(const QSvgRenderer);
520
0
    if (d->render)
521
0
        return d->render->viewBox();
522
0
    else
523
0
        return QRect();
524
0
}
525
526
void QSvgRenderer::setViewBox(const QRectF &viewbox)
527
0
{
528
0
    Q_D(QSvgRenderer);
529
0
    if (d->render)
530
0
        d->render->setViewBox(viewbox);
531
0
}
532
533
/*!
534
    \since 4.2
535
536
    Returns bounding rectangle of the item with the given \a id.
537
    The transformation matrix of parent elements is not affecting
538
    the bounds of the element.
539
540
    \sa transformForElement()
541
*/
542
QRectF QSvgRenderer::boundsOnElement(const QString &id) const
543
0
{
544
0
    Q_D(const QSvgRenderer);
545
0
    QRectF bounds;
546
0
    if (d->render)
547
0
        bounds = d->render->boundsOnElement(id);
548
0
    return bounds;
549
0
}
550
551
552
/*!
553
    \since 4.2
554
555
    Returns true if the element with the given \a id exists
556
    in the currently parsed SVG file and is a renderable
557
    element.
558
559
    Note: this method returns true only for elements that
560
    can be rendered. Which implies that elements that are considered
561
    part of the fill/stroke style properties, e.g. radialGradients
562
    even tough marked with "id" attributes will not be found by this
563
    method.
564
*/
565
bool QSvgRenderer::elementExists(const QString &id) const
566
0
{
567
0
    Q_D(const QSvgRenderer);
568
0
    bool exists = false;
569
0
    if (d->render)
570
0
        exists = d->render->elementExists(id);
571
0
    return exists;
572
0
}
573
574
/*!
575
    \since 5.15
576
577
    Returns the transformation matrix for the element
578
    with the given \a id. The matrix is a product of
579
    the transformation of the element's parents. The transformation of
580
    the element itself is not included.
581
582
    To find the bounding rectangle of the element in logical coordinates,
583
    you can apply the matrix on the rectangle returned from boundsOnElement().
584
585
    \sa boundsOnElement()
586
*/
587
QTransform QSvgRenderer::transformForElement(const QString &id) const
588
0
{
589
0
    Q_D(const QSvgRenderer);
590
0
    QTransform trans;
591
0
    if (d->render)
592
0
        trans = d->render->transformForElement(id);
593
0
    return trans;
594
0
}
595
596
QT_END_NAMESPACE
597
598
#include "moc_qsvgrenderer.cpp"
599
600
#endif // QT_NO_SVGRENDERER