1/****************************************************************************
2**
3** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the QtSvg 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 Digia. For licensing terms and
14** conditions see http://qt.digia.com/licensing. For further information
15** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Digia gives you certain additional
26** rights. These rights are described in the Digia Qt LGPL Exception
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28**
29** GNU General Public License Usage
30** Alternatively, this file may be used under the terms of the GNU
31** General Public License version 3.0 as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL included in the
33** packaging of this file. Please review the following information to
34** ensure the GNU General Public License version 3.0 requirements will be
35** met: http://www.gnu.org/copyleft/gpl.html.
36**
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qsvgrenderer.h"
43
44#ifndef QT_NO_SVGRENDERER
45
46#include "qsvgtinydocument_p.h"
47
48#include "qbytearray.h"
49#include "qtimer.h"
50#include "qdebug.h"
51#include "private/qobject_p.h"
52
53
54QT_BEGIN_NAMESPACE
55
56/*!
57 \class QSvgRenderer
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 \o The animated() function indicates whether a drawing contains animation information.
90 \omit
91 \o The animationDuration() function provides the duration in milliseconds of the
92 animation, without taking any looping into account.
93 \o The \l currentFrame property contains the current frame of the animation.
94 \endomit
95 \o 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, {QtSvg Module}, {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("QSvgRenderer::setFramesPerSecond: Cannot set negative value %d", num);
254 return;
255 }
256 d->fps = num;
257}
258
259/*!
260 \property QSvgRenderer::currentFrame
261 \brief the current frame of the document's animation, or 0 if the document is not animated
262 \internal
263
264 \sa animationDuration(), framesPerSecond, animated()
265*/
266
267/*!
268 \internal
269*/
270int QSvgRenderer::currentFrame() const
271{
272 Q_D(const QSvgRenderer);
273 return d->render->currentFrame();
274}
275
276/*!
277 \internal
278*/
279void QSvgRenderer::setCurrentFrame(int frame)
280{
281 Q_D(QSvgRenderer);
282 d->render->setCurrentFrame(frame);
283}
284
285/*!
286 \internal
287
288 Returns the number of frames in the animation, or 0 if the current document is not
289 animated.
290
291 \sa animated(), framesPerSecond
292*/
293int QSvgRenderer::animationDuration() const
294{
295 Q_D(const QSvgRenderer);
296 return d->render->animationDuration();
297}
298
299/*!
300 \internal
301 \since 4.5
302
303 We can't have template functions, that's loadDocument(), as friends, for this
304 code, so we let this function be a friend of QSvgRenderer instead.
305 */
306void QSvgRendererPrivate::callRepaintNeeded(QSvgRenderer *const q)
307{
308 q->repaintNeeded();
309}
310
311template<typename TInputType>
312static bool loadDocument(QSvgRenderer *const q,
313 QSvgRendererPrivate *const d,
314 const TInputType &in)
315{
316 delete d->render;
317 d->render = QSvgTinyDocument::load(in);
318 if (d->render && d->render->animated() && d->fps > 0) {
319 if (!d->timer)
320 d->timer = new QTimer(q);
321 else
322 d->timer->stop();
323 q->connect(d->timer, SIGNAL(timeout()),
324 q, SIGNAL(repaintNeeded()));
325 d->timer->start(1000/d->fps);
326 } else if (d->timer) {
327 d->timer->stop();
328 }
329
330 //force first update
331 QSvgRendererPrivate::callRepaintNeeded(q);
332
333 return d->render;
334}
335
336/*!
337 Loads the SVG file specified by \a filename, returning true if the content
338 was successfully parsed; otherwise returns false.
339*/
340bool QSvgRenderer::load(const QString &filename)
341{
342 Q_D(QSvgRenderer);
343 return loadDocument(this, d, filename);
344}
345
346/*!
347 Loads the specified SVG format \a contents, returning true if the content
348 was successfully parsed; otherwise returns false.
349*/
350bool QSvgRenderer::load(const QByteArray &contents)
351{
352 Q_D(QSvgRenderer);
353 return loadDocument(this, d, contents);
354}
355
356/*!
357 Loads the specified SVG in \a contents, returning true if the content
358 was successfully parsed; otherwise returns false.
359
360 The reader will be used from where it currently is positioned. If \a contents
361 is \c null, behavior is undefined.
362
363 \since 4.5
364*/
365bool QSvgRenderer::load(QXmlStreamReader *contents)
366{
367 Q_D(QSvgRenderer);
368 return loadDocument(this, d, contents);
369}
370
371/*!
372 Renders the current document, or the current frame of an animated
373 document, using the given \a painter.
374*/
375void QSvgRenderer::render(QPainter *painter)
376{
377 Q_D(QSvgRenderer);
378 if (d->render) {
379 d->render->draw(painter);
380 }
381}
382
383/*!
384 \fn void QSvgRenderer::repaintNeeded()
385
386 This signal is emitted whenever the rendering of the document
387 needs to be updated, usually for the purposes of animation.
388*/
389
390/*!
391 Renders the given element with \a elementId using the given \a painter
392 on the specified \a bounds. If the bounding rectangle is not specified
393 the SVG element is mapped to the whole paint device.
394*/
395void QSvgRenderer::render(QPainter *painter, const QString &elementId,
396 const QRectF &bounds)
397{
398 Q_D(QSvgRenderer);
399 if (d->render) {
400 d->render->draw(painter, elementId, bounds);
401 }
402}
403
404/*!
405 Renders the current document, or the current frame of an animated
406 document, using the given \a painter on the specified \a bounds within
407 the painter. If the bounding rectangle is not specified
408 the SVG file is mapped to the whole paint device.
409*/
410void QSvgRenderer::render(QPainter *painter, const QRectF &bounds)
411{
412 Q_D(QSvgRenderer);
413 if (d->render) {
414 d->render->draw(painter, bounds);
415 }
416}
417
418QRectF QSvgRenderer::viewBoxF() const
419{
420 Q_D(const QSvgRenderer);
421 if (d->render)
422 return d->render->viewBox();
423 else
424 return QRect();
425}
426
427void QSvgRenderer::setViewBox(const QRectF &viewbox)
428{
429 Q_D(QSvgRenderer);
430 if (d->render)
431 d->render->setViewBox(viewbox);
432}
433
434/*!
435 \since 4.2
436
437 Returns bounding rectangle of the item with the given \a id.
438 The transformation matrix of parent elements is not affecting
439 the bounds of the element.
440
441 \sa matrixForElement()
442*/
443QRectF QSvgRenderer::boundsOnElement(const QString &id) const
444{
445 Q_D(const QSvgRenderer);
446 QRectF bounds;
447 if (d->render)
448 bounds = d->render->boundsOnElement(id);
449 return bounds;
450}
451
452
453/*!
454 \since 4.2
455
456 Returns true if the element with the given \a id exists
457 in the currently parsed SVG file and is a renderable
458 element.
459
460 Note: this method returns true only for elements that
461 can be rendered. Which implies that elements that are considered
462 part of the fill/stroke style properties, e.g. radialGradients
463 even tough marked with "id" attributes will not be found by this
464 method.
465*/
466bool QSvgRenderer::elementExists(const QString &id) const
467{
468 Q_D(const QSvgRenderer);
469 bool exists = false;
470 if (d->render)
471 exists = d->render->elementExists(id);
472 return exists;
473}
474
475/*!
476 \since 4.2
477
478 Returns the transformation matrix for the element
479 with the given \a id. The matrix is a product of
480 the transformation of the element's parents. The transformation of
481 the element itself is not included.
482
483 To find the bounding rectangle of the element in logical coordinates,
484 you can apply the matrix on the rectangle returned from boundsOnElement().
485
486 \sa boundsOnElement()
487*/
488QMatrix QSvgRenderer::matrixForElement(const QString &id) const
489{
490 Q_D(const QSvgRenderer);
491 QMatrix mat;
492 if (d->render)
493 mat = d->render->matrixForElement(id);
494 return mat;
495}
496
497QT_END_NAMESPACE
498
499#include "moc_qsvgrenderer.cpp"
500
501#endif // QT_NO_SVGRENDERER
502