1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui 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 The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40
41#include "qtextimagehandler_p.h"
42
43#include <qguiapplication.h>
44#include <qtextformat.h>
45#include <qpainter.h>
46#include <qdebug.h>
47#include <qfile.h>
48#include <private/qtextengine_p.h>
49#include <qpalette.h>
50#include <qthread.h>
51
52QT_BEGIN_NAMESPACE
53
54extern QString qt_findAtNxFile(const QString &baseFileName, qreal targetDevicePixelRatio,
55 qreal *sourceDevicePixelRatio);
56
57static inline QUrl fromLocalfileOrResources(QString path)
58{
59 if (path.startsWith(s: QLatin1String(":/"))) // auto-detect resources and convert them to url
60 path.prepend(s: QLatin1String("qrc"));
61 return QUrl(path);
62}
63
64static QPixmap getPixmap(QTextDocument *doc, const QTextImageFormat &format, const qreal devicePixelRatio = 1.0)
65{
66 qreal sourcePixelRatio = 1.0;
67 const QString name = qt_findAtNxFile(baseFileName: format.name(), targetDevicePixelRatio: devicePixelRatio, sourceDevicePixelRatio: &sourcePixelRatio);
68 const QUrl url = fromLocalfileOrResources(path: name);
69
70 QPixmap pm;
71 const QVariant data = doc->resource(type: QTextDocument::ImageResource, name: url);
72 if (data.userType() == QMetaType::QPixmap || data.userType() == QMetaType::QImage) {
73 pm = qvariant_cast<QPixmap>(v: data);
74 } else if (data.userType() == QMetaType::QByteArray) {
75 pm.loadFromData(buf: data.toByteArray());
76 }
77
78 if (pm.isNull()) {
79#if 0
80 QString context;
81 // ### Qt5
82 QTextBrowser *browser = qobject_cast<QTextBrowser *>(doc->parent());
83 if (browser)
84 context = browser->source().toString();
85#endif
86 // try direct loading
87 QImage img;
88 if (name.isEmpty() || !img.load(fileName: name))
89 return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/file-16.png"));
90
91 pm = QPixmap::fromImage(image: img);
92 doc->addResource(type: QTextDocument::ImageResource, name: url, resource: pm);
93 }
94
95 if (name.contains(s: QLatin1String("@2x")))
96 pm.setDevicePixelRatio(sourcePixelRatio);
97
98 return pm;
99}
100
101static QSize getPixmapSize(QTextDocument *doc, const QTextImageFormat &format)
102{
103 QPixmap pm;
104
105 const bool hasWidth = format.hasProperty(propertyId: QTextFormat::ImageWidth);
106 const int width = qRound(d: format.width());
107 const bool hasHeight = format.hasProperty(propertyId: QTextFormat::ImageHeight);
108 const int height = qRound(d: format.height());
109
110 QSize size(width, height);
111 if (!hasWidth || !hasHeight) {
112 pm = getPixmap(doc, format);
113 const int pmWidth = pm.width() / pm.devicePixelRatio();
114 const int pmHeight = pm.height() / pm.devicePixelRatio();
115
116 if (!hasWidth) {
117 if (!hasHeight)
118 size.setWidth(pmWidth);
119 else
120 size.setWidth(qRound(d: height * (pmWidth / (qreal) pmHeight)));
121 }
122 if (!hasHeight) {
123 if (!hasWidth)
124 size.setHeight(pmHeight);
125 else
126 size.setHeight(qRound(d: width * (pmHeight / (qreal) pmWidth)));
127 }
128 }
129
130 qreal scale = 1.0;
131 QPaintDevice *pdev = doc->documentLayout()->paintDevice();
132 if (pdev) {
133 if (pm.isNull())
134 pm = getPixmap(doc, format);
135 if (!pm.isNull())
136 scale = qreal(pdev->logicalDpiY()) / qreal(qt_defaultDpi());
137 }
138 size *= scale;
139
140 return size;
141}
142
143static QImage getImage(QTextDocument *doc, const QTextImageFormat &format, const qreal devicePixelRatio = 1.0)
144{
145 qreal sourcePixelRatio = 1.0;
146 const QString name = qt_findAtNxFile(baseFileName: format.name(), targetDevicePixelRatio: devicePixelRatio, sourceDevicePixelRatio: &sourcePixelRatio);
147 const QUrl url = fromLocalfileOrResources(path: name);
148
149 QImage image;
150 const QVariant data = doc->resource(type: QTextDocument::ImageResource, name: url);
151 if (data.userType() == QMetaType::QImage) {
152 image = qvariant_cast<QImage>(v: data);
153 } else if (data.userType() == QMetaType::QByteArray) {
154 image.loadFromData(data: data.toByteArray());
155 }
156
157 if (image.isNull()) {
158#if 0
159 QString context;
160 // ### Qt5
161 QTextBrowser *browser = qobject_cast<QTextBrowser *>(doc->parent());
162 if (browser)
163 context = browser->source().toString();
164#endif
165 // try direct loading
166
167 if (name.isEmpty() || !image.load(fileName: name))
168 return QImage(QLatin1String(":/qt-project.org/styles/commonstyle/images/file-16.png"));
169
170 doc->addResource(type: QTextDocument::ImageResource, name: url, resource: image);
171 }
172
173 if (sourcePixelRatio != 1.0)
174 image.setDevicePixelRatio(sourcePixelRatio);
175
176 return image;
177}
178
179static QSize getImageSize(QTextDocument *doc, const QTextImageFormat &format)
180{
181 QImage image;
182
183 const bool hasWidth = format.hasProperty(propertyId: QTextFormat::ImageWidth);
184 const int width = qRound(d: format.width());
185 const bool hasHeight = format.hasProperty(propertyId: QTextFormat::ImageHeight);
186 const int height = qRound(d: format.height());
187
188 QSize size(width, height);
189 if (!hasWidth || !hasHeight) {
190 image = getImage(doc, format);
191 if (!hasWidth)
192 size.setWidth(image.width() / image.devicePixelRatio());
193 if (!hasHeight)
194 size.setHeight(image.height() / image.devicePixelRatio());
195 }
196
197 qreal scale = 1.0;
198 QPaintDevice *pdev = doc->documentLayout()->paintDevice();
199 if (pdev) {
200 if (image.isNull())
201 image = getImage(doc, format);
202 if (!image.isNull())
203 scale = qreal(pdev->logicalDpiY()) / qreal(qt_defaultDpi());
204 }
205 size *= scale;
206
207 return size;
208}
209
210QTextImageHandler::QTextImageHandler(QObject *parent)
211 : QObject(parent)
212{
213}
214
215QSizeF QTextImageHandler::intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format)
216{
217 Q_UNUSED(posInDocument)
218 const QTextImageFormat imageFormat = format.toImageFormat();
219
220 if (QCoreApplication::instance()->thread() != QThread::currentThread())
221 return getImageSize(doc, format: imageFormat);
222 return getPixmapSize(doc, format: imageFormat);
223}
224
225QImage QTextImageHandler::image(QTextDocument *doc, const QTextImageFormat &imageFormat)
226{
227 Q_ASSERT(doc != nullptr);
228
229 return getImage(doc, format: imageFormat);
230}
231
232void QTextImageHandler::drawObject(QPainter *p, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format)
233{
234 Q_UNUSED(posInDocument)
235 const QTextImageFormat imageFormat = format.toImageFormat();
236
237 if (QCoreApplication::instance()->thread() != QThread::currentThread()) {
238 const QImage image = getImage(doc, format: imageFormat, devicePixelRatio: p->device()->devicePixelRatioF());
239 p->drawImage(targetRect: rect, image, sourceRect: image.rect());
240 } else {
241 const QPixmap pixmap = getPixmap(doc, format: imageFormat, devicePixelRatio: p->device()->devicePixelRatioF());
242 p->drawPixmap(targetRect: rect, pixmap, sourceRect: pixmap.rect());
243 }
244}
245
246QT_END_NAMESPACE
247

source code of qtbase/src/gui/text/qtextimagehandler.cpp