Qt 4.8
qsvgtinydocument.cpp
Go to the documentation of this file.
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 "qsvgtinydocument_p.h"
43 
44 #ifndef QT_NO_SVG
45 
46 #include "qsvghandler_p.h"
47 #include "qsvgfont_p.h"
48 
49 #include "qpainter.h"
50 #include "qfile.h"
51 #include "qbuffer.h"
52 #include "qbytearray.h"
53 #include "qqueue.h"
54 #include "qstack.h"
55 #include "qdebug.h"
56 
57 #ifndef QT_NO_COMPRESS
58 #include <zlib.h>
59 #endif
60 
62 
65  , m_widthPercent(false)
66  , m_heightPercent(false)
67  , m_animated(false)
68  , m_animationDuration(0)
69  , m_fps(30)
70 {
71 }
72 
74 {
75 }
76 
77 #ifndef QT_NO_COMPRESS
78 # ifdef QT_BUILD_INTERNAL
80 # else
82 # endif
83 
85 {
86  if (!device)
87  return QByteArray();
88 
89  if (!device->isOpen())
90  device->open(QIODevice::ReadOnly);
91 
92  Q_ASSERT(device->isOpen() && device->isReadable());
93 
94  static const int CHUNK_SIZE = 4096;
95  int zlibResult = Z_OK;
96 
97  QByteArray source;
98  QByteArray destination;
99 
100  // Initialize zlib stream struct
101  z_stream zlibStream;
102  zlibStream.next_in = Z_NULL;
103  zlibStream.avail_in = 0;
104  zlibStream.avail_out = 0;
105  zlibStream.zalloc = Z_NULL;
106  zlibStream.zfree = Z_NULL;
107  zlibStream.opaque = Z_NULL;
108 
109  // Adding 16 to the window size gives us gzip decoding
110  if (inflateInit2(&zlibStream, MAX_WBITS + 16) != Z_OK) {
111  qWarning("Cannot initialize zlib, because: %s",
112  (zlibStream.msg != NULL ? zlibStream.msg : "Unknown error"));
113  return QByteArray();
114  }
115 
116  bool stillMoreWorkToDo = true;
117  while (stillMoreWorkToDo) {
118 
119  if (!zlibStream.avail_in) {
120  source = device->read(CHUNK_SIZE);
121 
122  if (source.isEmpty())
123  break;
124 
125  zlibStream.avail_in = source.size();
126  zlibStream.next_in = reinterpret_cast<Bytef*>(source.data());
127  }
128 
129  do {
130  // Prepare the destination buffer
131  int oldSize = destination.size();
132  destination.resize(oldSize + CHUNK_SIZE);
133  zlibStream.next_out = reinterpret_cast<Bytef*>(
134  destination.data() + oldSize - zlibStream.avail_out);
135  zlibStream.avail_out += CHUNK_SIZE;
136 
137  zlibResult = inflate(&zlibStream, Z_NO_FLUSH);
138  switch (zlibResult) {
139  case Z_NEED_DICT:
140  case Z_DATA_ERROR:
141  case Z_STREAM_ERROR:
142  case Z_MEM_ERROR: {
143  inflateEnd(&zlibStream);
144  qWarning("Error while inflating gzip file: %s",
145  (zlibStream.msg != NULL ? zlibStream.msg : "Unknown error"));
146  destination.chop(zlibStream.avail_out);
147  return destination;
148  }
149  }
150 
151  // If the output buffer still has more room after calling inflate
152  // it means we have to provide more data, so exit the loop here
153  } while (!zlibStream.avail_out);
154 
155  if (zlibResult == Z_STREAM_END) {
156  // Make sure there are no more members to process before exiting
157  if (!(zlibStream.avail_in && inflateReset(&zlibStream) == Z_OK))
158  stillMoreWorkToDo = false;
159  }
160  }
161 
162  // Chop off trailing space in the buffer
163  destination.chop(zlibStream.avail_out);
164 
165  inflateEnd(&zlibStream);
166  return destination;
167 }
168 #endif
169 
171 {
172  QFile file(fileName);
173  if (!file.open(QFile::ReadOnly)) {
174  qWarning("Cannot open file '%s', because: %s",
175  qPrintable(fileName), qPrintable(file.errorString()));
176  return 0;
177  }
178 
179 #ifndef QT_NO_COMPRESS
180  if (fileName.endsWith(QLatin1String(".svgz"), Qt::CaseInsensitive)
181  || fileName.endsWith(QLatin1String(".svg.gz"), Qt::CaseInsensitive)) {
182  return load(qt_inflateGZipDataFrom(&file));
183  }
184 #endif
185 
186  QSvgTinyDocument *doc = 0;
187  QSvgHandler handler(&file);
188  if (handler.ok()) {
189  doc = handler.document();
190  doc->m_animationDuration = handler.animationDuration();
191  } else {
192  qWarning("Cannot read file '%s', because: %s (line %d)",
193  qPrintable(fileName), qPrintable(handler.errorString()), handler.lineNumber());
194  }
195  return doc;
196 }
197 
199 {
200 #ifndef QT_NO_COMPRESS
201  // Check for gzip magic number and inflate if appropriate
202  if (contents.startsWith("\x1f\x8b")) {
203  QBuffer buffer(const_cast<QByteArray *>(&contents));
204  return load(qt_inflateGZipDataFrom(&buffer));
205  }
206 #endif
207 
208  QSvgHandler handler(contents);
209 
210  QSvgTinyDocument *doc = 0;
211  if (handler.ok()) {
212  doc = handler.document();
213  doc->m_animationDuration = handler.animationDuration();
214  }
215  return doc;
216 }
217 
219 {
220  QSvgHandler handler(contents);
221 
222  QSvgTinyDocument *doc = 0;
223  if (handler.ok()) {
224  doc = handler.document();
225  doc->m_animationDuration = handler.animationDuration();
226  }
227  return doc;
228 }
229 
231 {
232  if (m_time.isNull()) {
233  m_time.start();
234  }
235 
237  return;
238 
239  p->save();
240  //sets default style on the painter
241  //### not the most optimal way
242  mapSourceToTarget(p, bounds);
244  pen.setMiterLimit(4);
245  p->setPen(pen);
246  p->setBrush(Qt::black);
250  applyStyle(p, m_states);
251  while (itr != m_renderers.end()) {
252  QSvgNode *node = *itr;
253  if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
254  node->draw(p, m_states);
255  ++itr;
256  }
257  revertStyle(p, m_states);
258  p->restore();
259 }
260 
261 
263  const QRectF &bounds)
264 {
265  QSvgNode *node = scopeNode(id);
266 
267  if (!node) {
268  qDebug("Couldn't find node %s. Skipping rendering.", qPrintable(id));
269  return;
270  }
271  if (m_time.isNull()) {
272  m_time.start();
273  }
274 
275  if (node->displayMode() == QSvgNode::NoneMode)
276  return;
277 
278  p->save();
279 
280  const QRectF elementBounds = node->transformedBounds();
281 
282  mapSourceToTarget(p, bounds, elementBounds);
283  QTransform originalTransform = p->worldTransform();
284 
285  //XXX set default style on the painter
287  pen.setMiterLimit(4);
288  p->setPen(pen);
289  p->setBrush(Qt::black);
292 
293  QStack<QSvgNode*> parentApplyStack;
294  QSvgNode *parent = node->parent();
295  while (parent) {
296  parentApplyStack.push(parent);
297  parent = parent->parent();
298  }
299 
300  for (int i = parentApplyStack.size() - 1; i >= 0; --i)
301  parentApplyStack[i]->applyStyle(p, m_states);
302 
303  // Reset the world transform so that our parents don't affect
304  // the position
305  QTransform currentTransform = p->worldTransform();
306  p->setWorldTransform(originalTransform);
307 
308  node->draw(p, m_states);
309 
310  p->setWorldTransform(currentTransform);
311 
312  for (int i = 0; i < parentApplyStack.size(); ++i)
313  parentApplyStack[i]->revertStyle(p, m_states);
314 
315  //p->fillRect(bounds.adjusted(-5, -5, 5, 5), QColor(0, 0, 255, 100));
316 
317  p->restore();
318 }
319 
320 
322 {
323  return DOC;
324 }
325 
326 void QSvgTinyDocument::setWidth(int len, bool percent)
327 {
328  m_size.setWidth(len);
329  m_widthPercent = percent;
330 }
331 
332 void QSvgTinyDocument::setHeight(int len, bool percent)
333 {
334  m_size.setHeight(len);
335  m_heightPercent = percent;
336 }
337 
339 {
340  m_viewBox = rect;
341 }
342 
344 {
345  m_fonts.insert(font->familyName(), font);
346 }
347 
349 {
350  return m_fonts[family];
351 }
352 
354 {
355  m_namedNodes.insert(id, node);
356 }
357 
359 {
360  return m_namedNodes.value(id);
361 }
362 
364 {
365  m_namedStyles.insert(id, style);
366 }
367 
369 {
370  return m_namedStyles.value(id);
371 }
372 
374 {
375  m_time.restart();
376 }
377 
379 {
380  return m_animated;
381 }
382 
384 {
385  m_animated = a;
386 }
387 
389 {
390  draw(p, QRectF());
391 }
392 
394 {
395  draw(p);
396 }
397 
398 void QSvgTinyDocument::mapSourceToTarget(QPainter *p, const QRectF &targetRect, const QRectF &sourceRect)
399 {
400  QRectF target = targetRect;
401  if (target.isNull()) {
402  QPaintDevice *dev = p->device();
403  QRectF deviceRect(0, 0, dev->width(), dev->height());
404  if (deviceRect.isNull()) {
405  if (sourceRect.isNull())
406  target = QRectF(QPointF(0, 0), size());
407  else
408  target = QRectF(QPointF(0, 0), sourceRect.size());
409  } else {
410  target = deviceRect;
411  }
412  }
413 
414  QRectF source = sourceRect;
415  if (source.isNull())
416  source = viewBox();
417 
418  if (source != target && !source.isNull()) {
419  QTransform transform;
420  transform.scale(target.width() / source.width(),
421  target.height() / source.height());
422  QRectF c2 = transform.mapRect(source);
423  p->translate(target.x() - c2.x(),
424  target.y() - c2.y());
425  p->scale(target.width() / source.width(),
426  target.height() / source.height());
427  }
428 }
429 
431 {
432  const QSvgNode *node = scopeNode(id);
433  if (!node)
434  node = this;
435  return node->transformedBounds();
436 }
437 
439 {
440  QSvgNode *node = scopeNode(id);
441 
442  return (node!=0);
443 }
444 
446 {
447  QSvgNode *node = scopeNode(id);
448 
449  if (!node) {
450  qDebug("Couldn't find node %s. Skipping rendering.", qPrintable(id));
451  return QMatrix();
452  }
453 
454  QTransform t;
455 
456  node = node->parent();
457  while (node) {
458  if (node->m_style.transform)
459  t *= node->m_style.transform->qtransform();
460  node = node->parent();
461  }
462 
463  return t.toAffine();
464 }
465 
467 {
468  double runningPercentage = qMin(m_time.elapsed()/double(m_animationDuration), 1.);
469 
470  int totalFrames = m_fps * m_animationDuration;
471 
472  return int(runningPercentage * totalFrames);
473 }
474 
476 {
477  int totalFrames = m_fps * m_animationDuration;
478  double framePercentage = frame/double(totalFrames);
479  double timeForFrame = m_animationDuration * framePercentage; //in S
480  timeForFrame *= 1000; //in ms
481  int timeToAdd = int(timeForFrame - m_time.elapsed());
482  m_time = m_time.addMSecs(timeToAdd);
483 }
484 
486 {
487  m_fps = num;
488 }
489 
491 
492 #endif // QT_NO_SVG
The QPainter class performs low-level painting on widgets and other paint devices.
Definition: qpainter.h:86
QPaintDevice * device() const
Returns the paint device on which this painter is currently painting, or 0 if the painter is not acti...
Definition: qpainter.cpp:1530
virtual QRectF transformedBounds(QPainter *p, QSvgExtraStates &states) const
Definition: qsvgnode.cpp:307
void chop(int n)
Removes n bytes from the end of the byte array.
qreal y() const
Returns the y-coordinate of the rectangle&#39;s top edge.
Definition: qrect.h:667
void mapSourceToTarget(QPainter *p, const QRectF &targetRect, const QRectF &sourceRect=QRectF())
Q_DECL_CONSTEXPR const T & qMin(const T &a, const T &b)
Definition: qglobal.h:1215
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
QSvgStyle m_style
Definition: qsvgnode_p.h:160
char * data()
Returns a pointer to the data stored in the byte array.
Definition: qbytearray.h:429
bool isReadable() const
Returns true if data can be read from the device; otherwise returns false.
Definition: qiodevice.cpp:544
void setFramesPerSecond(int num)
The QMatrix class specifies 2D transformations of a coordinate system.
Definition: qmatrix.h:61
bool open(OpenMode flags)
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition: qfile.cpp:1064
void setWidth(int len, bool percent)
void start()
Sets this time to the current time.
Definition: qdatetime.cpp:2070
bool isNull() const
Returns true if the time is null (i.
Definition: qdatetime.h:158
void setHeight(int len, bool percent)
int height() const
Definition: qpaintdevice.h:92
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:135
The QPointF class defines a point in the plane using floating point precision.
Definition: qpoint.h:214
QString errorString() const
Returns a human-readable description of the last device error that occurred.
Definition: qiodevice.cpp:1671
QMatrix matrixForElement(const QString &id) const
void restore()
Restores the current painter state (pops a saved state off the stack).
Definition: qpainter.cpp:1620
iterator begin()
Returns an STL-style iterator pointing to the first item in the list.
Definition: qlist.h:267
QSvgTinyDocument * document() const
void addNamedStyle(const QString &id, QSvgFillStyleProperty *style)
QSvgFont * svgFont(const QString &family) const
QLatin1String(DBUS_INTERFACE_DBUS))) Q_GLOBAL_STATIC_WITH_ARGS(QString
QSvgNode * scopeNode(const QString &id) const
The QStack class is a template class that provides a stack.
Definition: qcontainerfwd.h:63
long ASN1_INTEGER_get ASN1_INTEGER * a
The QBuffer class provides a QIODevice interface for a QByteArray.
Definition: qbuffer.h:57
void applyStyle(QPainter *p, QSvgExtraStates &states) const
Definition: qsvgnode.cpp:118
QSvgNode * parent() const
Definition: qsvgnode_p.h:183
The QString class provides a Unicode character string.
Definition: qstring.h:83
void setHeight(int h)
Sets the height to the given height.
Definition: qsize.h:135
static QByteArray qt_inflateGZipDataFrom(QIODevice *device)
#define Q_ASSERT(cond)
Definition: qglobal.h:1823
bool startsWith(const QByteArray &a) const
Returns true if this byte array starts with byte array ba; otherwise returns false.
The QPen class defines how a QPainter should draw lines and outlines of shapes.
Definition: qpen.h:64
const T value(const Key &key) const
Returns the value associated with the key.
Definition: qhash.h:606
void save()
Saves the current painter state (pushes the state onto a stack).
Definition: qpainter.cpp:1590
void setViewBox(const QRectF &rect)
int restart()
Sets this time to the current time and returns the number of milliseconds that have elapsed since the...
Definition: qdatetime.cpp:2095
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition: qhash.h:753
bool elementExists(const QString &id) const
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read...
Definition: qiodevice.cpp:791
void setWidth(int w)
Sets the width to the given width.
Definition: qsize.h:132
Q_CORE_EXPORT void qDebug(const char *,...)
QRect mapRect(const QRect &) const
Creates and returns a QRect object that is a copy of the given rectangle, mapped into the coordinate ...
void setRenderHint(RenderHint hint, bool on=true)
Sets the given render hint on the painter if on is true; otherwise clears the render hint...
Definition: qpainter.cpp:7620
QHash< QString, QSvgRefCounter< QSvgFillStyleProperty > > m_namedStyles
bool isVisible() const
Definition: qsvgnode_p.h:188
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
QRectF viewBox() const
const QMatrix & toAffine() const
Returns the QTransform as an affine matrix.
int width() const
Definition: qpaintdevice.h:91
The QRectF class defines a rectangle in the plane using floating point precision. ...
Definition: qrect.h:511
void setAnimated(bool a)
bool isOpen() const
Returns true if the device is open; otherwise returns false.
Definition: qiodevice.cpp:530
void addNamedNode(const QString &id, QSvgNode *node)
const QTransform & qtransform() const
Definition: qsvgstyle_p.h:624
QSvgRefCounter< QSvgTransformStyle > transform
Definition: qsvgstyle_p.h:778
void addSvgFont(QSvgFont *)
QSvgFillStyleProperty * namedStyle(const QString &id) const
#define CHUNK_SIZE
iterator end()
Returns an STL-style iterator pointing to the imaginary item after the last item in the list...
Definition: qlist.h:270
qreal height() const
Returns the height of the rectangle.
Definition: qrect.h:710
DisplayMode displayMode() const
Definition: qsvgnode.cpp:330
Q_CORE_EXPORT void qWarning(const char *,...)
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal...
Definition: qstring.h:654
qreal width() const
Returns the width of the rectangle.
Definition: qrect.h:707
virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const
void push(const T &t)
Adds element t to the top of the stack.
Definition: qstack.h:60
static int inflate(Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen)
Definition: qzip.cpp:189
The QList::iterator class provides an STL-style non-const iterator for QList and QQueue.
Definition: qlist.h:181
int elapsed() const
Returns the number of milliseconds that have elapsed since the last time start() or restart() was cal...
Definition: qdatetime.cpp:2123
QSvgExtraStates m_states
const QTransform & worldTransform() const
Returns the world transformation matrix.
Definition: qpainter.cpp:9652
The QFile class provides an interface for reading from and writing to files.
Definition: qfile.h:65
int lineNumber() const
qreal x() const
Returns the x-coordinate of the rectangle&#39;s left edge.
Definition: qrect.h:664
void setBrush(const QBrush &brush)
Sets the painter&#39;s brush to the given brush.
Definition: qpainter.cpp:4171
void resize(int size)
Sets the size of the byte array to size bytes.
void setPen(const QColor &color)
Sets the painter&#39;s pen to have style Qt::SolidLine, width 0 and the specified color.
Definition: qpainter.cpp:4047
#define Q_AUTOTEST_EXPORT
Definition: qglobal.h:1510
int animationDuration() const
void setWorldTransform(const QTransform &matrix, bool combine=false)
Sets the world transformation matrix.
Definition: qpainter.cpp:9630
QSizeF size() const
Returns the size of the rectangle.
Definition: qrect.h:713
int size() const
Returns the number of bytes in this byte array.
Definition: qbytearray.h:402
virtual bool open(OpenMode mode)
Opens the device and sets its OpenMode to mode.
Definition: qiodevice.cpp:570
QString errorString() const
void scale(qreal sx, qreal sy)
Scales the coordinate system by ({sx}, {sy}).
Definition: qpainter.cpp:3234
virtual void draw(QPainter *p, QSvgExtraStates &states)=0
QTime addMSecs(int ms) const
Returns a QTime object containing a time ms milliseconds later than the time of this object (or earli...
Definition: qdatetime.cpp:1803
bool isEmpty() const
Returns true if the byte array has size 0; otherwise returns false.
Definition: qbytearray.h:421
QTransform & scale(qreal sx, qreal sy)
Scales the coordinate system by sx horizontally and sy vertically, and returns a reference to the mat...
Definition: qtransform.cpp:485
bool ok() const
The QXmlStreamReader class provides a fast parser for reading well-formed XML via a simple streaming ...
Definition: qxmlstream.h:290
QString familyName() const
Definition: qsvgfont.cpp:66
virtual void draw(QPainter *p, QSvgExtraStates &)
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition: qstring.cpp:3796
QImageIOHandler * handler
The QIODevice class is the base interface class of all I/O devices in Qt.
Definition: qiodevice.h:66
static QSvgTinyDocument * load(const QString &file)
void revertStyle(QPainter *p, QSvgExtraStates &states) const
Definition: qsvgnode.cpp:123
#define qPrintable(string)
Definition: qglobal.h:1750
static QString fileName(const QString &fileUrl)
int currentFrame() const
int size() const
Returns the number of items in the vector.
Definition: qvector.h:137
bool isNull() const
Returns true if the rectangle is a null rectangle, otherwise returns false.
Definition: qrect.h:655
QHash< QString, QSvgNode * > m_namedNodes
QList< QSvgNode * > m_renderers
The QTransform class specifies 2D transformations of a coordinate system.
Definition: qtransform.h:65
void setMiterLimit(qreal limit)
Sets the miter limit of this pen to the given limit.
Definition: qpen.cpp:611
QHash< QString, QSvgRefCounter< QSvgFont > > m_fonts
QSvgNode * namedNode(const QString &id) const
void translate(const QPointF &offset)
Translates the coordinate system by the given offset; i.e.
Definition: qpainter.cpp:3311
QRectF boundsOnElement(const QString &id) const