Coverage Report

Created: 2026-05-31 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtsvg/src/svg/qsvgdocument.cpp
Line
Count
Source
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
4
#include "qsvgdocument_p.h"
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 "qstack.h"
14
#include "qtransform.h"
15
#include "qdebug.h"
16
17
#ifndef QT_NO_COMPRESS
18
#include <zlib.h>
19
#endif
20
21
QT_BEGIN_NAMESPACE
22
23
using namespace Qt::StringLiterals;
24
25
QSvgDocument::QSvgDocument(QtSvg::Options options, QtSvg::AnimatorType type)
26
37.7k
    : QSvgStructureNode(0)
27
37.7k
    , m_widthPercent(false)
28
37.7k
    , m_heightPercent(false)
29
37.7k
    , m_animated(false)
30
37.7k
    , m_fps(30)
31
37.7k
    , m_options(options)
32
37.7k
{
33
37.7k
    m_states.trustedSource = m_options.testFlag(QtSvg::AssumeTrustedSource);
34
37.7k
    bool animationEnabled = !m_options.testFlag(QtSvg::DisableAnimations);
35
37.7k
    switch (type) {
36
37.7k
        case QtSvg::AnimatorType::Automatic:
37
37.7k
            if (animationEnabled)
38
37.7k
                m_animator.reset(new QSvgAnimator);
39
37.7k
            break;
40
0
        case QtSvg::AnimatorType::Controlled:
41
0
            if (animationEnabled)
42
0
                m_animator.reset(new QSvgAnimationController);
43
37.7k
    }
44
37.7k
}
45
46
QSvgDocument::~QSvgDocument()
47
37.7k
{
48
    // Only do that when AssumeTrustedSource is set to false. Otherwise, all nodes
49
    // will be deleted by recursive calls of destructors.
50
37.7k
    if (!m_states.trustedSource)
51
37.7k
        releaseDescendants();
52
37.7k
}
53
54
static bool hasSvgHeader(const QByteArray &buf)
55
99.4k
{
56
99.4k
    QTextStream s(buf); // Handle multi-byte encodings
57
99.4k
    QString h = s.readAll();
58
99.4k
    QStringView th = QStringView(h).trimmed();
59
99.4k
    bool matched = false;
60
99.4k
    if (th.startsWith("<svg"_L1) || th.startsWith("<!DOCTYPE svg"_L1))
61
48.7k
        matched = true;
62
50.7k
    else if (th.startsWith("<?xml"_L1) || th.startsWith("<!--"_L1))
63
9.17k
        matched = th.contains("<!DOCTYPE svg"_L1) || th.contains("<svg"_L1);
64
99.4k
    return matched;
65
99.4k
}
66
67
#ifndef QT_NO_COMPRESS
68
static QByteArray qt_inflateSvgzDataFrom(QIODevice *device, bool doCheckContent = true);
69
#   ifdef QT_BUILD_INTERNAL
70
Q_AUTOTEST_EXPORT QByteArray qt_inflateGZipDataFrom(QIODevice *device)
71
{
72
    return qt_inflateSvgzDataFrom(device, false); // autotest wants unchecked result
73
}
74
#   endif
75
76
static QByteArray qt_inflateSvgzDataFrom(QIODevice *device, bool doCheckContent)
77
2.09k
{
78
2.09k
    if (!device)
79
0
        return QByteArray();
80
81
2.09k
    if (!device->isOpen())
82
2.09k
        device->open(QIODevice::ReadOnly);
83
84
2.09k
    Q_ASSERT(device->isOpen() && device->isReadable());
85
86
2.09k
    static const int CHUNK_SIZE = 4096;
87
2.09k
    int zlibResult = Z_OK;
88
89
2.09k
    QByteArray source;
90
2.09k
    QByteArray destination;
91
92
    // Initialize zlib stream struct
93
2.09k
    z_stream zlibStream;
94
2.09k
    zlibStream.next_in = Z_NULL;
95
2.09k
    zlibStream.avail_in = 0;
96
2.09k
    zlibStream.avail_out = 0;
97
2.09k
    zlibStream.zalloc = Z_NULL;
98
2.09k
    zlibStream.zfree = Z_NULL;
99
2.09k
    zlibStream.opaque = Z_NULL;
100
101
    // Adding 16 to the window size gives us gzip decoding
102
2.09k
    if (inflateInit2(&zlibStream, MAX_WBITS + 16) != Z_OK) {
103
0
        qCWarning(lcSvgHandler, "Cannot initialize zlib, because: %s",
104
0
                (zlibStream.msg != NULL ? zlibStream.msg : "Unknown error"));
105
0
        return QByteArray();
106
0
    }
107
108
2.09k
    bool stillMoreWorkToDo = true;
109
7.07k
    while (stillMoreWorkToDo) {
110
111
7.07k
        if (!zlibStream.avail_in) {
112
7.07k
            source = device->read(CHUNK_SIZE);
113
114
7.07k
            if (source.isEmpty())
115
2.05k
                break;
116
117
5.02k
            zlibStream.avail_in = source.size();
118
5.02k
            zlibStream.next_in = reinterpret_cast<Bytef*>(source.data());
119
5.02k
        }
120
121
6.40k
        do {
122
            // Prepare the destination buffer
123
6.40k
            int oldSize = destination.size();
124
6.40k
            if (oldSize > INT_MAX - CHUNK_SIZE) {
125
0
                inflateEnd(&zlibStream);
126
0
                qCWarning(lcSvgHandler, "Error while inflating gzip file: integer size overflow");
127
0
                return QByteArray();
128
0
            }
129
130
6.40k
            destination.resize(oldSize + CHUNK_SIZE);
131
6.40k
            zlibStream.next_out = reinterpret_cast<Bytef*>(
132
6.40k
                    destination.data() + oldSize - zlibStream.avail_out);
133
6.40k
            zlibStream.avail_out += CHUNK_SIZE;
134
135
6.40k
            zlibResult = inflate(&zlibStream, Z_NO_FLUSH);
136
6.40k
            switch (zlibResult) {
137
0
                case Z_NEED_DICT:
138
43
                case Z_DATA_ERROR:
139
43
                case Z_STREAM_ERROR:
140
43
                case Z_MEM_ERROR: {
141
43
                    inflateEnd(&zlibStream);
142
43
                    qCWarning(lcSvgHandler, "Error while inflating gzip file: %s",
143
43
                            (zlibStream.msg != NULL ? zlibStream.msg : "Unknown error"));
144
43
                    return QByteArray();
145
43
                }
146
6.40k
            }
147
148
        // If the output buffer still has more room after calling inflate
149
        // it means we have to provide more data, so exit the loop here
150
6.40k
        } while (!zlibStream.avail_out);
151
152
4.97k
        if (doCheckContent) {
153
            // Quick format check, equivalent to QSvgIOHandler::canRead()
154
2.09k
            const qsizetype destinationContents = std::min(destination.size(), static_cast<qsizetype>(zlibStream.total_out));
155
2.09k
            Q_ASSERT(destinationContents == static_cast<qsizetype>(zlibStream.total_out));
156
2.09k
            if (!hasSvgHeader(QByteArray::fromRawData(destination.constData(), destinationContents))) {
157
0
                inflateEnd(&zlibStream);
158
0
                qCWarning(lcSvgHandler, "Error while inflating gzip file: SVG format check failed");
159
0
                return QByteArray();
160
0
            }
161
2.09k
            doCheckContent = false; // Run only once, on first chunk
162
2.09k
        }
163
164
4.97k
        if (zlibResult == Z_STREAM_END) {
165
            // Make sure there are no more members to process before exiting
166
0
            if (!(zlibStream.avail_in && inflateReset(&zlibStream) == Z_OK))
167
0
                stillMoreWorkToDo = false;
168
0
        }
169
4.97k
    }
170
171
    // Chop off trailing space in the buffer
172
2.05k
    destination.chop(zlibStream.avail_out);
173
174
2.05k
    inflateEnd(&zlibStream);
175
2.05k
    return destination;
176
2.09k
}
177
#else
178
static QByteArray qt_inflateSvgzDataFrom(QIODevice *)
179
{
180
    return QByteArray();
181
}
182
#endif
183
184
std::unique_ptr<QSvgDocument> QSvgDocument::load(const QString &fileName, QtSvg::Options options,
185
                                         QtSvg::AnimatorType type)
186
9.64k
{
187
9.64k
    std::unique_ptr<QSvgDocument> doc;
188
9.64k
    QFile file(fileName);
189
9.64k
    if (!file.open(QFile::ReadOnly)) {
190
0
        qCWarning(lcSvgHandler, "Cannot open file '%s', because: %s",
191
0
                  qPrintable(fileName), qPrintable(file.errorString()));
192
0
        return doc;
193
0
    }
194
195
9.64k
    if (fileName.endsWith(QLatin1String(".svgz"), Qt::CaseInsensitive)
196
9.64k
            || fileName.endsWith(QLatin1String(".svg.gz"), Qt::CaseInsensitive)) {
197
0
        return load(qt_inflateSvgzDataFrom(&file));
198
0
    }
199
200
9.64k
    QSvgHandler handler(&file, options, type);
201
9.64k
    if (handler.ok()) {
202
1.93k
        doc.reset(handler.document());
203
1.93k
        if (doc->m_animator)
204
1.93k
            doc->m_animator->setAnimationDuration(handler.animationDuration());
205
7.70k
    } else {
206
7.70k
        qCWarning(lcSvgHandler, "Cannot read file '%s', because: %s (line %d)",
207
7.70k
                 qPrintable(fileName), qPrintable(handler.errorString()), handler.lineNumber());
208
7.70k
        delete handler.document();
209
7.70k
    }
210
9.64k
    return doc;
211
9.64k
}
212
213
std::unique_ptr<QSvgDocument> QSvgDocument::load(const QByteArray &contents, QtSvg::Options options,
214
                                         QtSvg::AnimatorType type)
215
10.7k
{
216
10.7k
    std::unique_ptr<QSvgDocument> doc;
217
10.7k
    QByteArray svg;
218
    // Check for gzip magic number and inflate if appropriate
219
10.7k
    if (contents.startsWith("\x1f\x8b")) {
220
2.09k
        QBuffer buffer;
221
2.09k
        buffer.setData(contents);
222
2.09k
        svg = qt_inflateSvgzDataFrom(&buffer);
223
8.63k
    } else {
224
8.63k
        svg = contents;
225
8.63k
    }
226
10.7k
    if (svg.isNull())
227
43
        return doc;
228
229
10.6k
    QBuffer buffer;
230
10.6k
    buffer.setData(svg);
231
10.6k
    buffer.open(QIODevice::ReadOnly);
232
10.6k
    QSvgHandler handler(&buffer, options, type);
233
234
10.6k
    if (handler.ok()) {
235
22
        doc.reset(handler.document());
236
22
        if (doc->m_animator)
237
22
            doc->m_animator->setAnimationDuration(handler.animationDuration());
238
10.6k
    } else {
239
10.6k
        delete handler.document();
240
10.6k
    }
241
10.6k
    return doc;
242
10.7k
}
243
244
std::unique_ptr<QSvgDocument> QSvgDocument::load(QXmlStreamReader *contents, QtSvg::Options options,
245
                                         QtSvg::AnimatorType type)
246
21.3k
{
247
21.3k
    QSvgHandler handler(contents, options, type);
248
249
21.3k
    std::unique_ptr<QSvgDocument> doc;
250
21.3k
    if (handler.ok()) {
251
96
        doc.reset(handler.document());
252
96
        if (doc->m_animator)
253
96
            doc->m_animator->setAnimationDuration(handler.animationDuration());
254
21.3k
    } else {
255
21.3k
        delete handler.document();
256
21.3k
    }
257
21.3k
    return doc;
258
21.3k
}
259
260
void QSvgDocument::draw(QPainter *p, const QRectF &bounds)
261
1.93k
{
262
1.93k
    if (displayMode() == QSvgNode::NoneMode)
263
0
        return;
264
265
1.93k
    p->save();
266
    //sets default style on the painter
267
    //### not the most optimal way
268
1.93k
    mapSourceToTarget(p, bounds);
269
1.93k
    initPainter(p);
270
1.93k
    applyStyle(p, m_states);
271
61.9k
    for (const auto &node : renderers()) {
272
61.9k
        if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
273
61.9k
            node->draw(p, m_states);
274
61.9k
    }
275
1.93k
    revertStyle(p, m_states);
276
1.93k
    p->restore();
277
1.93k
}
278
279
280
void QSvgDocument::draw(QPainter *p, const QString &id,
281
                            const QRectF &bounds)
282
0
{
283
0
    QSvgNode *node = scopeNode(id);
284
285
0
    if (!node) {
286
0
        qCDebug(lcSvgHandler, "Couldn't find node %s. Skipping rendering.", qPrintable(id));
287
0
        return;
288
0
    }
289
290
0
    if (node->displayMode() == QSvgNode::NoneMode)
291
0
        return;
292
293
0
    p->save();
294
295
0
    const QRectF elementBounds = node->bounds();
296
297
0
    mapSourceToTarget(p, bounds, elementBounds);
298
0
    QTransform originalTransform = p->worldTransform();
299
300
    //XXX set default style on the painter
301
0
    QPen pen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
302
0
    pen.setMiterLimit(4);
303
0
    p->setPen(pen);
304
0
    p->setBrush(Qt::black);
305
0
    p->setRenderHint(QPainter::Antialiasing);
306
0
    p->setRenderHint(QPainter::SmoothPixmapTransform);
307
308
0
    QStack<QSvgNode*> parentApplyStack;
309
0
    QSvgNode *parent = node->parent();
310
0
    while (parent) {
311
0
        parentApplyStack.push(parent);
312
0
        parent = parent->parent();
313
0
    }
314
315
0
    for (int i = parentApplyStack.size() - 1; i >= 0; --i)
316
0
        parentApplyStack[i]->applyStyle(p, m_states);
317
318
    // Reset the world transform so that our parents don't affect
319
    // the position
320
0
    QTransform currentTransform = p->worldTransform();
321
0
    p->setWorldTransform(originalTransform);
322
323
0
    node->draw(p, m_states);
324
325
0
    p->setWorldTransform(currentTransform);
326
327
0
    for (int i = 0; i < parentApplyStack.size(); ++i)
328
0
        parentApplyStack[i]->revertStyle(p, m_states);
329
330
    //p->fillRect(bounds.adjusted(-5, -5, 5, 5), QColor(0, 0, 255, 100));
331
332
0
    p->restore();
333
0
}
334
335
QSvgNode::Type QSvgDocument::type() const
336
1.62M
{
337
1.62M
    return Doc;
338
1.62M
}
339
340
void QSvgDocument::setWidth(int len, bool percent)
341
137
{
342
137
    m_size.setWidth(len);
343
137
    m_widthPercent = percent;
344
137
}
345
346
void QSvgDocument::setHeight(int len, bool percent)
347
116
{
348
116
    m_size.setHeight(len);
349
116
    m_heightPercent = percent;
350
116
}
351
352
void QSvgDocument::setPreserveAspectRatio(bool on)
353
0
{
354
0
    m_preserveAspectRatio = on;
355
0
}
356
357
void QSvgDocument::setViewBox(const QRectF &rect)
358
4.18k
{
359
4.18k
    m_viewBox = rect;
360
4.18k
    m_implicitViewBox = rect.isNull();
361
4.18k
}
362
363
QtSvg::Options QSvgDocument::options() const
364
404k
{
365
404k
    return m_options;
366
404k
}
367
368
void QSvgDocument::addSvgFont(QSvgFont *font)
369
0
{
370
0
    m_fonts.emplace(font->familyName(), font);
371
0
}
372
373
QSvgFont * QSvgDocument::svgFont(const QString &family) const
374
0
{
375
0
    return m_fonts[family];
376
0
}
377
378
void QSvgDocument::addNamedNode(const QString &id, QSvgNode *node)
379
131k
{
380
131k
    m_namedNodes.insert(id, node);
381
131k
}
382
383
QSvgNode *QSvgDocument::namedNode(const QString &id) const
384
85.6k
{
385
85.6k
    return m_namedNodes.value(id);
386
85.6k
}
387
388
void QSvgDocument::addPaintServer(QSvgPaintServerSharedPtr paintServer, const QString &id)
389
0
{
390
0
    if (id.isEmpty())
391
0
        return;
392
393
0
    if (!m_paintServers.insert({ id, paintServer } ).second)
394
0
        qCWarning(lcSvgHandler) << "Duplicate unique style id:" << id;
395
0
}
396
397
QSvgPaintServerSharedPtr QSvgDocument::paintServer(QStringView id) const
398
27.3k
{
399
27.3k
    auto it = m_paintServers.find(id.toString());
400
27.3k
    return it != m_paintServers.end() ? it->second : nullptr;
401
27.3k
}
402
403
void QSvgDocument::restartAnimation()
404
2.05k
{
405
2.05k
    if (m_animator)
406
2.05k
        m_animator->restartAnimation();
407
2.05k
}
408
409
bool QSvgDocument::animated() const
410
107k
{
411
107k
    return m_animated;
412
107k
}
413
414
void QSvgDocument::setAnimated(bool a)
415
0
{
416
0
    m_animated = a;
417
0
}
418
419
void QSvgDocument::draw(QPainter *p)
420
0
{
421
0
    draw(p, QRectF());
422
0
}
423
424
void QSvgDocument::drawCommand(QPainter *, QSvgExtraStates &)
425
0
{
426
0
    qCDebug(lcSvgHandler) << "SVG Tiny does not support nested <svg> elements: ignored.";
427
0
    return;
428
0
}
429
430
static bool isValidMatrix(const QTransform &transform)
431
1.93k
{
432
1.93k
    qreal determinant = transform.determinant();
433
1.93k
    return qIsFinite(determinant);
434
1.93k
}
435
436
void QSvgDocument::mapSourceToTarget(QPainter *p, const QRectF &targetRect, const QRectF &sourceRect)
437
1.93k
{
438
1.93k
    QTransform oldTransform = p->worldTransform();
439
440
1.93k
    QRectF target = targetRect;
441
1.93k
    if (target.isEmpty()) {
442
126
        QPaintDevice *dev = p->device();
443
126
        QRectF deviceRect(0, 0, dev->width(), dev->height());
444
126
        if (deviceRect.isEmpty()) {
445
126
            if (sourceRect.isEmpty())
446
126
                target = QRectF(QPointF(0, 0), size());
447
0
            else
448
0
                target = QRectF(QPointF(0, 0), sourceRect.size());
449
126
        } else {
450
0
            target = deviceRect;
451
0
        }
452
126
    }
453
454
1.93k
    QRectF source = sourceRect;
455
1.93k
    if (source.isEmpty())
456
1.93k
        source = viewBox();
457
458
1.93k
    if (source != target && !qFuzzyIsNull(source.width()) && !qFuzzyIsNull(source.height())) {
459
1.44k
        if (m_implicitViewBox || !preserveAspectRatio()) {
460
            // Code path used when no view box is set, or IgnoreAspectRatio requested
461
1.44k
            QTransform transform;
462
1.44k
            transform.scale(target.width() / source.width(),
463
1.44k
                            target.height() / source.height());
464
1.44k
            QRectF c2 = transform.mapRect(source);
465
1.44k
            p->translate(target.x() - c2.x(),
466
1.44k
                         target.y() - c2.y());
467
1.44k
            p->scale(target.width() / source.width(),
468
1.44k
                     target.height() / source.height());
469
1.44k
        } else {
470
            // Code path used when KeepAspectRatio is requested. This attempts to emulate the default values
471
            // of the <preserveAspectRatio tag that's implicitly defined when <viewbox> is used.
472
473
            // Scale the view box into the view port (target) by preserve the aspect ratio.
474
0
            QSizeF viewBoxSize = source.size();
475
0
            viewBoxSize.scale(target.width(), target.height(), Qt::KeepAspectRatio);
476
477
            // Center the view box in the view port
478
0
            p->translate(target.x() + (target.width() - viewBoxSize.width()) / 2,
479
0
                         target.y() + (target.height() - viewBoxSize.height()) / 2);
480
481
0
            p->scale(viewBoxSize.width() / source.width(),
482
0
                     viewBoxSize.height() / source.height());
483
484
            // Apply the view box translation if specified.
485
0
            p->translate(-source.x(), -source.y());
486
0
        }
487
1.44k
    }
488
489
1.93k
    if (!isValidMatrix(p->worldTransform()))
490
0
        p->setWorldTransform(oldTransform);
491
1.93k
}
492
493
QRectF QSvgDocument::boundsOnElement(const QString &id) const
494
0
{
495
0
    const QSvgNode *node = scopeNode(id);
496
0
    if (!node)
497
0
        node = this;
498
0
    return node->bounds();
499
0
}
500
501
bool QSvgDocument::elementExists(const QString &id) const
502
0
{
503
0
    QSvgNode *node = scopeNode(id);
504
505
0
    return (node!=0);
506
0
}
507
508
QTransform QSvgDocument::transformForElement(const QString &id) const
509
0
{
510
0
    QSvgNode *node = scopeNode(id);
511
512
0
    if (!node) {
513
0
        qCDebug(lcSvgHandler, "Couldn't find node %s. Skipping rendering.", qPrintable(id));
514
0
        return QTransform();
515
0
    }
516
517
0
    QTransform t;
518
519
0
    node = node->parent();
520
0
    while (node) {
521
0
        if (node->m_style.transform)
522
0
            t *= node->m_style.transform->qtransform();
523
0
        node = node->parent();
524
0
    }
525
526
0
    return t;
527
0
}
528
529
int QSvgDocument::currentFrame() const
530
0
{
531
0
    const double runningPercentage = qMin(currentElapsed() / double(animationDuration()), 1.);
532
0
    const int totalFrames = m_fps * animationDuration() / 1000;
533
0
    return int(runningPercentage * totalFrames);
534
0
}
535
536
void QSvgDocument::setCurrentFrame(int frame)
537
0
{
538
0
    if (!m_animator)
539
0
        return;
540
541
0
    const int totalFrames = m_fps * animationDuration() / 1000;
542
0
    if (totalFrames == 0)
543
0
        return;
544
545
0
    const int timeForFrame = frame * animationDuration() / totalFrames; //in ms
546
0
    const int timeToAdd = timeForFrame - currentElapsed();
547
0
    m_animator->setAnimatorTime(timeToAdd);
548
0
}
549
550
void QSvgDocument::setFramesPerSecond(int num)
551
0
{
552
0
    m_fps = num;
553
0
}
554
555
QSharedPointer<QSvgAbstractAnimator> QSvgDocument::animator() const
556
3.86k
{
557
3.86k
    return m_animator;
558
3.86k
}
559
560
bool QSvgDocument::isLikelySvg(QIODevice *device, bool *isCompressed)
561
126k
{
562
126k
    constexpr int bufSize = 4096;
563
126k
    char buf[bufSize];
564
126k
    char inflateBuf[bufSize];
565
126k
    bool useInflateBuf = false;
566
126k
    int readLen = device->peek(buf, bufSize);
567
126k
    if (readLen < 8)
568
28.4k
        return false;
569
98.5k
#ifndef QT_NO_COMPRESS
570
98.5k
    if (quint8(buf[0]) == 0x1f && quint8(buf[1]) == 0x8b) {
571
        // Indicates gzip compressed content, i.e. svgz
572
5.50k
        z_stream zlibStream;
573
5.50k
        zlibStream.avail_in = readLen;
574
5.50k
        zlibStream.next_out = reinterpret_cast<Bytef *>(inflateBuf);
575
5.50k
        zlibStream.avail_out = bufSize;
576
5.50k
        zlibStream.next_in = reinterpret_cast<Bytef *>(buf);
577
5.50k
        zlibStream.zalloc = Z_NULL;
578
5.50k
        zlibStream.zfree = Z_NULL;
579
5.50k
        zlibStream.opaque = Z_NULL;
580
5.50k
        if (inflateInit2(&zlibStream, MAX_WBITS + 16) != Z_OK)
581
0
            return false;
582
5.50k
        int zlibResult = inflate(&zlibStream, Z_NO_FLUSH);
583
5.50k
        inflateEnd(&zlibStream);
584
5.50k
        if ((zlibResult != Z_OK && zlibResult != Z_STREAM_END) || zlibStream.total_out < 8)
585
1.14k
            return false;
586
4.35k
        readLen = zlibStream.total_out;
587
4.35k
        if (isCompressed)
588
2.09k
            *isCompressed = true;
589
4.35k
        useInflateBuf = true;
590
4.35k
    }
591
97.3k
#endif
592
97.3k
    return hasSvgHeader(QByteArray::fromRawData(useInflateBuf ? inflateBuf : buf, readLen));
593
98.5k
}
594
595
QT_END_NAMESPACE