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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtsvg/src/svg/qsvgrenderer.cpp