Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qsvgtinydocument.cpp
Go to the documentation of this file.
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
5
6#include "qsvghandler_p.h"
7#include "qsvgfont_p.h"
8
9#include "qpainter.h"
10#include "qfile.h"
11#include "qbuffer.h"
12#include "qbytearray.h"
13#include "qqueue.h"
14#include "qstack.h"
15#include "qtransform.h"
16#include "qdebug.h"
17
18#ifndef QT_NO_COMPRESS
19#include <zlib.h>
20#endif
21
23
26 , m_widthPercent(false)
27 , m_heightPercent(false)
28 , m_time(0)
29 , m_animated(false)
30 , m_animationDuration(0)
31 , m_fps(30)
32{
33}
34
36{
37}
38
39#ifndef QT_NO_COMPRESS
40static QByteArray qt_inflateSvgzDataFrom(QIODevice *device, bool doCheckContent = true);
41# ifdef QT_BUILD_INTERNAL
42Q_AUTOTEST_EXPORT QByteArray qt_inflateGZipDataFrom(QIODevice *device)
43{
44 return qt_inflateSvgzDataFrom(device, false); // autotest wants unchecked result
45}
46# endif
47
49{
50 if (!device)
51 return QByteArray();
52
53 if (!device->isOpen())
55
56 Q_ASSERT(device->isOpen() && device->isReadable());
57
58 static const int CHUNK_SIZE = 4096;
59 int zlibResult = Z_OK;
60
63
64 // Initialize zlib stream struct
65 z_stream zlibStream;
66 zlibStream.next_in = Z_NULL;
67 zlibStream.avail_in = 0;
68 zlibStream.avail_out = 0;
69 zlibStream.zalloc = Z_NULL;
70 zlibStream.zfree = Z_NULL;
71 zlibStream.opaque = Z_NULL;
72
73 // Adding 16 to the window size gives us gzip decoding
74 if (inflateInit2(&zlibStream, MAX_WBITS + 16) != Z_OK) {
75 qCWarning(lcSvgHandler, "Cannot initialize zlib, because: %s",
76 (zlibStream.msg != NULL ? zlibStream.msg : "Unknown error"));
77 return QByteArray();
78 }
79
80 bool stillMoreWorkToDo = true;
81 while (stillMoreWorkToDo) {
82
83 if (!zlibStream.avail_in) {
84 source = device->read(CHUNK_SIZE);
85
86 if (source.isEmpty())
87 break;
88
89 zlibStream.avail_in = source.size();
90 zlibStream.next_in = reinterpret_cast<Bytef*>(source.data());
91 }
92
93 do {
94 // Prepare the destination buffer
95 int oldSize = destination.size();
96 if (oldSize > INT_MAX - CHUNK_SIZE) {
97 inflateEnd(&zlibStream);
98 qCWarning(lcSvgHandler, "Error while inflating gzip file: integer size overflow");
99 return QByteArray();
100 }
101
102 destination.resize(oldSize + CHUNK_SIZE);
103 zlibStream.next_out = reinterpret_cast<Bytef*>(
104 destination.data() + oldSize - zlibStream.avail_out);
105 zlibStream.avail_out += CHUNK_SIZE;
106
107 zlibResult = inflate(&zlibStream, Z_NO_FLUSH);
108 switch (zlibResult) {
109 case Z_NEED_DICT:
110 case Z_DATA_ERROR:
111 case Z_STREAM_ERROR:
112 case Z_MEM_ERROR: {
113 inflateEnd(&zlibStream);
114 qCWarning(lcSvgHandler, "Error while inflating gzip file: %s",
115 (zlibStream.msg != NULL ? zlibStream.msg : "Unknown error"));
116 return QByteArray();
117 }
118 }
119
120 // If the output buffer still has more room after calling inflate
121 // it means we have to provide more data, so exit the loop here
122 } while (!zlibStream.avail_out);
123
124 if (doCheckContent) {
125 // Quick format check, equivalent to QSvgIOHandler::canRead()
126 QByteArray buf = destination.left(16);
127 if (!buf.contains("<?xml") && !buf.contains("<svg") && !buf.contains("<!--") && !buf.contains("<!DOCTYPE svg")) {
128 inflateEnd(&zlibStream);
129 qCWarning(lcSvgHandler, "Error while inflating gzip file: SVG format check failed");
130 return QByteArray();
131 }
132 doCheckContent = false; // Run only once, on first chunk
133 }
134
135 if (zlibResult == Z_STREAM_END) {
136 // Make sure there are no more members to process before exiting
137 if (!(zlibStream.avail_in && inflateReset(&zlibStream) == Z_OK))
138 stillMoreWorkToDo = false;
139 }
140 }
141
142 // Chop off trailing space in the buffer
143 destination.chop(zlibStream.avail_out);
144
145 inflateEnd(&zlibStream);
146 return destination;
147}
148#else
150{
151 return QByteArray();
152}
153#endif
154
156{
158 if (!file.open(QFile::ReadOnly)) {
159 qCWarning(lcSvgHandler, "Cannot open file '%s', because: %s",
161 return 0;
162 }
163
164 if (fileName.endsWith(QLatin1String(".svgz"), Qt::CaseInsensitive)
165 || fileName.endsWith(QLatin1String(".svg.gz"), Qt::CaseInsensitive)) {
167 }
168
169 QSvgTinyDocument *doc = nullptr;
170 QSvgHandler handler(&file);
171 if (handler.ok()) {
172 doc = handler.document();
173 doc->m_animationDuration = handler.animationDuration();
174 } else {
175 qCWarning(lcSvgHandler, "Cannot read file '%s', because: %s (line %d)",
176 qPrintable(fileName), qPrintable(handler.errorString()), handler.lineNumber());
177 delete handler.document();
178 }
179 return doc;
180}
181
183{
184 QByteArray svg;
185 // Check for gzip magic number and inflate if appropriate
186 if (contents.startsWith("\x1f\x8b")) {
188 buffer.setData(contents);
190 } else {
191 svg = contents;
192 }
193 if (svg.isNull())
194 return nullptr;
195
197 buffer.setData(svg);
199 QSvgHandler handler(&buffer);
200
201 QSvgTinyDocument *doc = nullptr;
202 if (handler.ok()) {
203 doc = handler.document();
204 doc->m_animationDuration = handler.animationDuration();
205 } else {
206 delete handler.document();
207 }
208 return doc;
209}
210
212{
213 QSvgHandler handler(contents);
214
215 QSvgTinyDocument *doc = nullptr;
216 if (handler.ok()) {
217 doc = handler.document();
218 doc->m_animationDuration = handler.animationDuration();
219 } else {
220 delete handler.document();
221 }
222 return doc;
223}
224
226{
227 if (m_time == 0)
229
231 return;
232
233 p->save();
234 //sets default style on the painter
235 //### not the most optimal way
236 mapSourceToTarget(p, bounds);
238 pen.setMiterLimit(4);
239 p->setPen(pen);
240 p->setBrush(Qt::black);
241 p->setRenderHint(QPainter::Antialiasing);
242 p->setRenderHint(QPainter::SmoothPixmapTransform);
244 applyStyle(p, m_states);
245 while (itr != m_renderers.end()) {
246 QSvgNode *node = *itr;
247 if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
248 node->draw(p, m_states);
249 ++itr;
250 }
251 revertStyle(p, m_states);
252 p->restore();
253}
254
255
257 const QRectF &bounds)
258{
259 QSvgNode *node = scopeNode(id);
260
261 if (!node) {
262 qCDebug(lcSvgHandler, "Couldn't find node %s. Skipping rendering.", qPrintable(id));
263 return;
264 }
265 if (m_time == 0)
267
268 if (node->displayMode() == QSvgNode::NoneMode)
269 return;
270
271 p->save();
272
273 const QRectF elementBounds = node->transformedBounds();
274
275 mapSourceToTarget(p, bounds, elementBounds);
276 QTransform originalTransform = p->worldTransform();
277
278 //XXX set default style on the painter
280 pen.setMiterLimit(4);
281 p->setPen(pen);
282 p->setBrush(Qt::black);
283 p->setRenderHint(QPainter::Antialiasing);
284 p->setRenderHint(QPainter::SmoothPixmapTransform);
285
286 QStack<QSvgNode*> parentApplyStack;
287 QSvgNode *parent = node->parent();
288 while (parent) {
289 parentApplyStack.push(parent);
290 parent = parent->parent();
291 }
292
293 for (int i = parentApplyStack.size() - 1; i >= 0; --i)
294 parentApplyStack[i]->applyStyle(p, m_states);
295
296 // Reset the world transform so that our parents don't affect
297 // the position
298 QTransform currentTransform = p->worldTransform();
299 p->setWorldTransform(originalTransform);
300
301 node->draw(p, m_states);
302
303 p->setWorldTransform(currentTransform);
304
305 for (int i = 0; i < parentApplyStack.size(); ++i)
306 parentApplyStack[i]->revertStyle(p, m_states);
307
308 //p->fillRect(bounds.adjusted(-5, -5, 5, 5), QColor(0, 0, 255, 100));
309
310 p->restore();
311}
312
313
315{
316 return DOC;
317}
318
319void QSvgTinyDocument::setWidth(int len, bool percent)
320{
321 m_size.setWidth(len);
322 m_widthPercent = percent;
323}
324
325void QSvgTinyDocument::setHeight(int len, bool percent)
326{
327 m_size.setHeight(len);
328 m_heightPercent = percent;
329}
330
332{
333 m_preserveAspectRatio = on;
334}
335
337{
338 m_viewBox = rect;
339 m_implicitViewBox = rect.isNull();
340}
341
343{
344 m_fonts.insert(font->familyName(), font);
345}
346
348{
349 return m_fonts[family];
350}
351
353{
354 m_namedNodes.insert(id, node);
355}
356
358{
359 return m_namedNodes.value(id);
360}
361
363{
364 if (!m_namedStyles.contains(id))
365 m_namedStyles.insert(id, style);
366 else
367 qCWarning(lcSvgHandler) << "Duplicate unique style id:" << id;
368}
369
371{
372 return m_namedStyles.value(id);
373}
374
376{
378}
379
381{
382 return m_animated;
383}
384
386{
387 m_animated = a;
388}
389
391{
392 draw(p, QRectF());
393}
394
396{
397 draw(p);
398}
399
401{
402 qreal determinant = transform.determinant();
403 return qIsFinite(determinant);
404}
405
406void QSvgTinyDocument::mapSourceToTarget(QPainter *p, const QRectF &targetRect, const QRectF &sourceRect)
407{
408 QTransform oldTransform = p->worldTransform();
409
410 QRectF target = targetRect;
411 if (target.isEmpty()) {
412 QPaintDevice *dev = p->device();
413 QRectF deviceRect(0, 0, dev->width(), dev->height());
414 if (deviceRect.isEmpty()) {
415 if (sourceRect.isEmpty())
416 target = QRectF(QPointF(0, 0), size());
417 else
418 target = QRectF(QPointF(0, 0), sourceRect.size());
419 } else {
421 }
422 }
423
424 QRectF source = sourceRect;
425 if (source.isEmpty())
426 source = viewBox();
427
428 if (source != target && !qFuzzyIsNull(source.width()) && !qFuzzyIsNull(source.height())) {
429 if (m_implicitViewBox || !preserveAspectRatio()) {
430 // Code path used when no view box is set, or IgnoreAspectRatio requested
432 transform.scale(target.width() / source.width(),
433 target.height() / source.height());
434 QRectF c2 = transform.mapRect(source);
435 p->translate(target.x() - c2.x(),
436 target.y() - c2.y());
437 p->scale(target.width() / source.width(),
438 target.height() / source.height());
439 } else {
440 // Code path used when KeepAspectRatio is requested. This attempts to emulate the default values
441 // of the <preserveAspectRatio tag that's implicitly defined when <viewbox> is used.
442
443 // Scale the view box into the view port (target) by preserve the aspect ratio.
444 QSizeF viewBoxSize = source.size();
445 viewBoxSize.scale(target.width(), target.height(), Qt::KeepAspectRatio);
446
447 // Center the view box in the view port
448 p->translate(target.x() + (target.width() - viewBoxSize.width()) / 2,
449 target.y() + (target.height() - viewBoxSize.height()) / 2);
450
451 p->scale(viewBoxSize.width() / source.width(),
452 viewBoxSize.height() / source.height());
453
454 // Apply the view box translation if specified.
455 p->translate(-source.x(), -source.y());
456 }
457 }
458
459 if (!isValidMatrix(p->worldTransform()))
460 p->setWorldTransform(oldTransform);
461}
462
464{
465 const QSvgNode *node = scopeNode(id);
466 if (!node)
467 node = this;
468 return node->transformedBounds();
469}
470
472{
473 QSvgNode *node = scopeNode(id);
474
475 return (node!=0);
476}
477
479{
480 QSvgNode *node = scopeNode(id);
481
482 if (!node) {
483 qCDebug(lcSvgHandler, "Couldn't find node %s. Skipping rendering.", qPrintable(id));
484 return QTransform();
485 }
486
488
489 node = node->parent();
490 while (node) {
491 if (node->m_style.transform)
492 t *= node->m_style.transform->qtransform();
493 node = node->parent();
494 }
495
496 return t;
497}
498
500{
501 double runningPercentage = qMin(currentElapsed() / double(m_animationDuration), 1.);
502
503 int totalFrames = m_fps * m_animationDuration;
504
505 return int(runningPercentage * totalFrames);
506}
507
509{
510 int totalFrames = m_fps * m_animationDuration;
511 double framePercentage = frame/double(totalFrames);
512 double timeForFrame = m_animationDuration * framePercentage; //in S
513 timeForFrame *= 1000; //in ms
514 int timeToAdd = int(timeForFrame - currentElapsed());
515 m_time += timeToAdd;
516}
517
519{
520 m_fps = num;
521}
522
IOBluetoothDevice * device
\inmodule QtCore \reentrant
Definition qbuffer.h:16
\inmodule QtCore
Definition qbytearray.h:57
bool isNull() const noexcept
Returns true if this byte array is null; otherwise returns false.
static qint64 currentMSecsSinceEpoch() noexcept
\inmodule QtCore
Definition qfile.h:93
bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:881
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:991
T value(const Key &key) const noexcept
Definition qhash.h:1044
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
\inmodule QtCore \reentrant
Definition qiodevice.h:34
QString errorString() const
Returns a human-readable description of the last device error that occurred.
qsizetype size() const noexcept
Definition qlist.h:386
iterator end()
Definition qlist.h:609
iterator begin()
Definition qlist.h:608
int width() const
int height() const
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
@ SmoothPixmapTransform
Definition qpainter.h:54
@ Antialiasing
Definition qpainter.h:52
\inmodule QtGui
Definition qpen.h:25
void setMiterLimit(qreal limit)
Sets the miter limit of this pen to the given limit.
Definition qpen.cpp:570
\inmodule QtCore\reentrant
Definition qpoint.h:214
\inmodule QtCore\reentrant
Definition qrect.h:483
constexpr bool isEmpty() const noexcept
Returns true if the rectangle is empty, otherwise returns false.
Definition qrect.h:647
constexpr QSizeF size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:721
constexpr bool isEmpty() const noexcept
Returns true if the rectangle is empty, otherwise returns false.
Definition qrect.h:166
\inmodule QtCore
Definition qsize.h:207
void scale(qreal w, qreal h, Qt::AspectRatioMode mode) noexcept
Scales the size to a rectangle with the given width and height, according to the specified mode.
Definition qsize.h:336
constexpr qreal width() const noexcept
Returns the width.
Definition qsize.h:321
constexpr qreal height() const noexcept
Returns the height.
Definition qsize.h:324
constexpr void setWidth(int w) noexcept
Sets the width to the given width.
Definition qsize.h:135
constexpr void setHeight(int h) noexcept
Sets the height to the given height.
Definition qsize.h:138
\inmodule QtCore
Definition qstack.h:13
void push(const T &t)
Adds element t to the top of the stack.
Definition qstack.h:17
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QSvgTinyDocument * document() const
bool ok() const
int animationDuration() const
int lineNumber() const
QString errorString() const
void revertStyle(QPainter *p, QSvgExtraStates &states) const
Definition qsvgnode.cpp:104
virtual QRectF transformedBounds(QPainter *p, QSvgExtraStates &states) const
Definition qsvgnode.cpp:293
QSvgNode * parent() const
Definition qsvgnode_p.h:148
void applyStyle(QPainter *p, QSvgExtraStates &states) const
Definition qsvgnode.cpp:99
virtual void draw(QPainter *p, QSvgExtraStates &states)=0
DisplayMode displayMode() const
Definition qsvgnode.cpp:316
QSvgStyle m_style
Definition qsvgnode_p.h:125
bool isVisible() const
Definition qsvgnode_p.h:153
QRectF bounds(QPainter *p, QSvgExtraStates &states) const override
QSvgNode * scopeNode(const QString &id) const
QList< QSvgNode * > m_renderers
QSvgRefCounter< QSvgTransformStyle > transform
bool elementExists(const QString &id) const
QSvgNode * namedNode(const QString &id) const
void setFramesPerSecond(int num)
Type type() const override
void setViewBox(const QRectF &rect)
void setWidth(int len, bool percent)
void draw(QPainter *p, QSvgExtraStates &) override
QRectF boundsOnElement(const QString &id) const
bool preserveAspectRatio() const
void addNamedNode(const QString &id, QSvgNode *node)
QSvgFont * svgFont(const QString &family) const
QRectF viewBox() const
void setPreserveAspectRatio(bool on)
QSvgFillStyleProperty * namedStyle(const QString &id) const
void addSvgFont(QSvgFont *)
QTransform transformForElement(const QString &id) const
static QSvgTinyDocument * load(const QString &file)
void addNamedStyle(const QString &id, QSvgFillStyleProperty *style)
void setHeight(int len, bool percent)
const QTransform & qtransform() const
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
rect
[4]
Combined button and popup list for selecting options.
@ KeepAspectRatio
@ black
Definition qnamespace.h:29
@ SolidLine
@ SvgMiterJoin
@ CaseInsensitive
@ NoBrush
@ FlatCap
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char * destination
bool qIsFinite(qfloat16 f) noexcept
Definition qfloat16.h:239
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:303
#define qCWarning(category,...)
#define qCDebug(category,...)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint id
[7]
GLenum GLuint buffer
GLenum GLuint GLenum GLsizei const GLchar * buf
GLenum target
GLsizei GLsizei GLchar * source
GLuint GLenum GLenum transform
GLenum GLsizei len
GLdouble GLdouble t
Definition qopenglext.h:243
GLfloat GLfloat p
[1]
GLuint num
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define CHUNK_SIZE
#define qPrintable(string)
Definition qstring.h:1391
static QByteArray qt_inflateSvgzDataFrom(QIODevice *device, bool doCheckContent=true)
static bool isValidMatrix(const QTransform &transform)
#define Q_AUTOTEST_EXPORT
double qreal
Definition qtypes.h:92
static int inflate(Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen)
Definition qzip.cpp:92
QFile file
[0]
MyCustomStruct c2
QFrame frame
[0]