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

source code of qtsvg/src/plugins/imageformats/svg/qsvgiohandler.cpp