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 | |
54 | QT_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 | |
104 | class QSvgRendererPrivate : public QObjectPrivate |
105 | { |
106 | Q_DECLARE_PUBLIC(QSvgRenderer) |
107 | public: |
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 | */ |
128 | QSvgRenderer::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 | */ |
137 | QSvgRenderer::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 | */ |
147 | QSvgRenderer::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 | */ |
159 | QSvgRenderer::QSvgRenderer(QXmlStreamReader *contents, QObject *parent) |
160 | : QObject(*new QSvgRendererPrivate, parent) |
161 | { |
162 | load(contents); |
163 | } |
164 | |
165 | /*! |
166 | Destroys the renderer. |
167 | */ |
168 | QSvgRenderer::~QSvgRenderer() |
169 | { |
170 | |
171 | } |
172 | |
173 | /*! |
174 | Returns true if there is a valid current document; otherwise returns false. |
175 | */ |
176 | bool 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 | */ |
185 | QSize 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 | */ |
199 | QRect 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 | */ |
213 | void 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 | */ |
226 | bool 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 | */ |
243 | int QSvgRenderer::framesPerSecond() const |
244 | { |
245 | Q_D(const QSvgRenderer); |
246 | return d->fps; |
247 | } |
248 | |
249 | void 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 | */ |
270 | int QSvgRenderer::currentFrame() const |
271 | { |
272 | Q_D(const QSvgRenderer); |
273 | return d->render->currentFrame(); |
274 | } |
275 | |
276 | /*! |
277 | \internal |
278 | */ |
279 | void 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 | */ |
293 | int 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 | */ |
306 | void QSvgRendererPrivate::callRepaintNeeded(QSvgRenderer *const q) |
307 | { |
308 | q->repaintNeeded(); |
309 | } |
310 | |
311 | template<typename TInputType> |
312 | static 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 | */ |
340 | bool 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 | */ |
350 | bool 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 | */ |
365 | bool 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 | */ |
375 | void 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 | */ |
395 | void 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 | */ |
410 | void 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 | |
418 | QRectF 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 | |
427 | void 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 | */ |
443 | QRectF 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 | */ |
466 | bool 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 | */ |
488 | QMatrix 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 | |
497 | QT_END_NAMESPACE |
498 | |
499 | #include "moc_qsvgrenderer.cpp" |
500 | |
501 | #endif // QT_NO_SVGRENDERER |
502 | |