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

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