Coverage Report

Created: 2026-03-12 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtsvg/src/plugins/imageformats/svg/qsvgiohandler.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
// Qt-Security score:significant reason:default
4
5
6
#include "qsvgiohandler.h"
7
8
#ifndef QT_NO_SVGRENDERER
9
10
#include "qsvgrenderer.h"
11
#include "private/qsvgdocument_p.h"
12
#include "qimage.h"
13
#include "qpixmap.h"
14
#include "qpainter.h"
15
#include "qvariant.h"
16
#include "qbuffer.h"
17
#include "qdebug.h"
18
19
QT_BEGIN_NAMESPACE
20
21
class QSvgIOHandlerPrivate
22
{
23
public:
24
    QSvgIOHandlerPrivate(QSvgIOHandler *qq)
25
32.4k
        : q(qq), loadAttempted(false), loadStatus(false), readDone(false), backColor(Qt::transparent)
26
32.4k
    {}
27
28
    bool load(QIODevice *device);
29
30
    QSvgIOHandler   *q;
31
    QSvgRenderer     r;
32
    QXmlStreamReader xmlReader;
33
    QSize            defaultSize;
34
    QRect            clipRect;
35
    QSize            scaledSize;
36
    QRect            scaledClipRect;
37
    bool             loadAttempted;
38
    bool             loadStatus;
39
    bool             readDone;
40
    QColor           backColor;
41
};
42
43
44
bool QSvgIOHandlerPrivate::load(QIODevice *device)
45
32.4k
{
46
32.4k
    if (loadAttempted)
47
0
        return loadStatus;
48
32.4k
    loadAttempted = true;
49
32.4k
    if (q->format().isEmpty())
50
7.35k
        q->canRead();
51
52
    // # The SVG renderer doesn't handle trailing, unrelated data, so we must
53
    // assume that all available data in the device is to be read.
54
32.4k
    bool res = false;
55
32.4k
    QBuffer *buf = qobject_cast<QBuffer *>(device);
56
32.4k
    if (buf) {
57
7.35k
        const QByteArray &ba = buf->data();
58
7.35k
        res = r.load(QByteArray::fromRawData(ba.constData() + buf->pos(), ba.size() - buf->pos()));
59
7.35k
        buf->seek(ba.size());
60
7.35k
#ifndef QT_NO_COMPRESS
61
25.1k
    } else if (q->format() == "svgz") {
62
2.44k
        res = r.load(device->readAll());
63
2.44k
#endif
64
22.7k
    } else {
65
22.7k
        xmlReader.setDevice(device);
66
22.7k
        res = r.load(&xmlReader);
67
22.7k
    }
68
69
32.4k
    if (res) {
70
95
        defaultSize = r.defaultSize();
71
95
        loadStatus = true;
72
95
    }
73
74
32.4k
    return loadStatus;
75
32.4k
}
76
77
78
QSvgIOHandler::QSvgIOHandler()
79
32.4k
    : d(new QSvgIOHandlerPrivate(this))
80
32.4k
{
81
82
32.4k
}
83
84
85
QSvgIOHandler::~QSvgIOHandler()
86
32.4k
{
87
32.4k
    delete d;
88
32.4k
}
89
90
bool QSvgIOHandler::canRead() const
91
32.4k
{
92
32.4k
    if (!device())
93
0
        return false;
94
32.4k
    if (d->loadStatus && !d->readDone)
95
0
        return true;        // Will happen if we have been asked for the size
96
97
32.4k
    bool isCompressed = false;
98
32.4k
    if (QSvgDocument::isLikelySvg(device(), &isCompressed)) {
99
32.4k
        setFormat(isCompressed ? "svgz" : "svg");
100
32.4k
        return true;
101
32.4k
    }
102
0
    return false;
103
32.4k
}
104
105
bool QSvgIOHandler::read(QImage *image)
106
32.4k
{
107
32.4k
    if (!d->readDone && d->load(device())) {
108
95
        bool xform = (d->clipRect.isValid() || d->scaledSize.isValid() || d->scaledClipRect.isValid());
109
95
        QSize finalSize = d->defaultSize;
110
95
        QRectF bounds;
111
95
        if (xform && !d->defaultSize.isEmpty()) {
112
0
            bounds = QRectF(QPointF(0,0), QSizeF(d->defaultSize));
113
0
            QPoint tr1, tr2;
114
0
            QSizeF sc(1, 1);
115
0
            if (d->clipRect.isValid()) {
116
0
                tr1 = -d->clipRect.topLeft();
117
0
                finalSize = d->clipRect.size();
118
0
            }
119
0
            if (d->scaledSize.isValid()) {
120
0
                sc = QSizeF(qreal(d->scaledSize.width()) / finalSize.width(),
121
0
                            qreal(d->scaledSize.height()) / finalSize.height());
122
0
                finalSize = d->scaledSize;
123
0
            }
124
0
            if (d->scaledClipRect.isValid()) {
125
0
                tr2 = -d->scaledClipRect.topLeft();
126
0
                finalSize = d->scaledClipRect.size();
127
0
            }
128
0
            QTransform t;
129
0
            t.translate(tr2.x(), tr2.y());
130
0
            t.scale(sc.width(), sc.height());
131
0
            t.translate(tr1.x(), tr1.y());
132
0
            bounds = t.mapRect(bounds);
133
0
        }
134
95
        if (finalSize.isEmpty()) {
135
95
            *image = QImage();
136
95
        } else {
137
0
            if (qMax(finalSize.width(), finalSize.height()) > 0xffff)
138
0
                return false; // Assume corrupted file
139
0
            if (!QImageIOHandler::allocateImage(finalSize, QImage::Format_ARGB32_Premultiplied, image))
140
0
                return false;
141
0
            image->fill(d->backColor.rgba());
142
0
            QPainter p(image);
143
0
            d->r.render(&p, bounds);
144
0
            p.end();
145
0
        }
146
95
        d->readDone = true;
147
95
        return true;
148
95
    }
149
150
32.4k
    return false;
151
32.4k
}
152
153
154
QVariant QSvgIOHandler::option(ImageOption option) const
155
0
{
156
0
    switch(option) {
157
0
    case ImageFormat:
158
0
        return QImage::Format_ARGB32_Premultiplied;
159
0
        break;
160
0
    case Size:
161
0
        d->load(device());
162
0
        return d->defaultSize;
163
0
        break;
164
0
    case ClipRect:
165
0
        return d->clipRect;
166
0
        break;
167
0
    case ScaledSize:
168
0
        return d->scaledSize;
169
0
        break;
170
0
    case ScaledClipRect:
171
0
        return d->scaledClipRect;
172
0
        break;
173
0
    case BackgroundColor:
174
0
        return d->backColor;
175
0
        break;
176
0
    default:
177
0
        break;
178
0
    }
179
0
    return QVariant();
180
0
}
181
182
183
void QSvgIOHandler::setOption(ImageOption option, const QVariant & value)
184
0
{
185
0
    switch(option) {
186
0
    case ClipRect:
187
0
        d->clipRect = value.toRect();
188
0
        break;
189
0
    case ScaledSize:
190
0
        d->scaledSize = value.toSize();
191
0
        break;
192
0
    case ScaledClipRect:
193
0
        d->scaledClipRect = value.toRect();
194
0
        break;
195
0
    case BackgroundColor:
196
0
        d->backColor = value.value<QColor>();
197
0
        break;
198
0
    default:
199
0
        break;
200
0
    }
201
0
}
202
203
204
bool QSvgIOHandler::supportsOption(ImageOption option) const
205
130k
{
206
130k
    switch(option)
207
130k
    {
208
0
    case ImageFormat:
209
0
    case Size:
210
32.4k
    case ClipRect:
211
64.9k
    case ScaledSize:
212
97.4k
    case ScaledClipRect:
213
97.4k
    case BackgroundColor:
214
97.4k
        return true;
215
32.5k
    default:
216
32.5k
        break;
217
130k
    }
218
32.5k
    return false;
219
130k
}
220
221
222
bool QSvgIOHandler::canRead(QIODevice *device)
223
75.3k
{
224
75.3k
    return QSvgDocument::isLikelySvg(device);
225
75.3k
}
226
227
QT_END_NAMESPACE
228
229
#endif // QT_NO_SVGRENDERER