Coverage Report

Created: 2025-11-16 07:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtbase/src/gui/text/qtextimagehandler.cpp
Line
Count
Source
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
52
QT_BEGIN_NAMESPACE
53
54
extern QString qt_findAtNxFile(const QString &baseFileName, qreal targetDevicePixelRatio,
55
                               qreal *sourceDevicePixelRatio);
56
57
static inline QUrl fromLocalfileOrResources(QString path)
58
0
{
59
0
    if (path.startsWith(QLatin1String(":/"))) // auto-detect resources and convert them to url
60
0
        path.prepend(QLatin1String("qrc"));
61
0
    return QUrl(path);
62
0
}
63
64
static QPixmap getPixmap(QTextDocument *doc, const QTextImageFormat &format, const qreal devicePixelRatio = 1.0)
65
0
{
66
0
    qreal sourcePixelRatio = 1.0;
67
0
    const QString name = qt_findAtNxFile(format.name(), devicePixelRatio, &sourcePixelRatio);
68
0
    const QUrl url = fromLocalfileOrResources(name);
69
70
0
    QPixmap pm;
71
0
    const QVariant data = doc->resource(QTextDocument::ImageResource, url);
72
0
    if (data.userType() == QMetaType::QPixmap || data.userType() == QMetaType::QImage) {
73
0
        pm = qvariant_cast<QPixmap>(data);
74
0
    } else if (data.userType() == QMetaType::QByteArray) {
75
0
        pm.loadFromData(data.toByteArray());
76
0
    }
77
78
0
    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
0
        QImage img;
88
0
        if (name.isEmpty() || !img.load(name))
89
0
            return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/file-16.png"));
90
91
0
        pm = QPixmap::fromImage(img);
92
0
        doc->addResource(QTextDocument::ImageResource, url, pm);
93
0
    }
94
95
0
    if (name.contains(QLatin1String("@2x")))
96
0
        pm.setDevicePixelRatio(sourcePixelRatio);
97
98
0
    return pm;
99
0
}
100
101
static QSize getPixmapSize(QTextDocument *doc, const QTextImageFormat &format)
102
0
{
103
0
    QPixmap pm;
104
105
0
    const bool hasWidth = format.hasProperty(QTextFormat::ImageWidth);
106
0
    const int width = qRound(format.width());
107
0
    const bool hasHeight = format.hasProperty(QTextFormat::ImageHeight);
108
0
    const int height = qRound(format.height());
109
110
0
    QSize size(width, height);
111
0
    if (!hasWidth || !hasHeight) {
112
0
        pm = getPixmap(doc, format);
113
0
        const int pmWidth = pm.width() / pm.devicePixelRatio();
114
0
        const int pmHeight = pm.height() / pm.devicePixelRatio();
115
116
0
        if (!hasWidth) {
117
0
            if (!hasHeight)
118
0
                size.setWidth(pmWidth);
119
0
            else
120
0
                size.setWidth(qRound(height * (pmWidth / (qreal) pmHeight)));
121
0
        }
122
0
        if (!hasHeight) {
123
0
            if (!hasWidth)
124
0
                size.setHeight(pmHeight);
125
0
            else
126
0
                size.setHeight(qRound(width * (pmHeight / (qreal) pmWidth)));
127
0
        }
128
0
    }
129
130
0
    qreal scale = 1.0;
131
0
    QPaintDevice *pdev = doc->documentLayout()->paintDevice();
132
0
    if (pdev) {
133
0
        if (pm.isNull())
134
0
            pm = getPixmap(doc, format);
135
0
        if (!pm.isNull())
136
0
            scale = qreal(pdev->logicalDpiY()) / qreal(qt_defaultDpi());
137
0
    }
138
0
    size *= scale;
139
140
0
    return size;
141
0
}
142
143
static QImage getImage(QTextDocument *doc, const QTextImageFormat &format, const qreal devicePixelRatio = 1.0)
144
0
{
145
0
    qreal sourcePixelRatio = 1.0;
146
0
    const QString name = qt_findAtNxFile(format.name(), devicePixelRatio, &sourcePixelRatio);
147
0
    const QUrl url = fromLocalfileOrResources(name);
148
149
0
    QImage image;
150
0
    const QVariant data = doc->resource(QTextDocument::ImageResource, url);
151
0
    if (data.userType() == QMetaType::QImage) {
152
0
        image = qvariant_cast<QImage>(data);
153
0
    } else if (data.userType() == QMetaType::QByteArray) {
154
0
        image.loadFromData(data.toByteArray());
155
0
    }
156
157
0
    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
0
        if (name.isEmpty() || !image.load(name))
168
0
            return QImage(QLatin1String(":/qt-project.org/styles/commonstyle/images/file-16.png"));
169
170
0
        doc->addResource(QTextDocument::ImageResource, url, image);
171
0
    }
172
173
0
    if (sourcePixelRatio != 1.0)
174
0
        image.setDevicePixelRatio(sourcePixelRatio);
175
176
0
    return image;
177
0
}
178
179
static QSize getImageSize(QTextDocument *doc, const QTextImageFormat &format)
180
0
{
181
0
    QImage image;
182
183
0
    const bool hasWidth = format.hasProperty(QTextFormat::ImageWidth);
184
0
    const int width = qRound(format.width());
185
0
    const bool hasHeight = format.hasProperty(QTextFormat::ImageHeight);
186
0
    const int height = qRound(format.height());
187
188
0
    QSize size(width, height);
189
0
    if (!hasWidth || !hasHeight) {
190
0
        image = getImage(doc, format);
191
0
        if (!hasWidth)
192
0
            size.setWidth(image.width() / image.devicePixelRatio());
193
0
        if (!hasHeight)
194
0
            size.setHeight(image.height() / image.devicePixelRatio());
195
0
    }
196
197
0
    qreal scale = 1.0;
198
0
    QPaintDevice *pdev = doc->documentLayout()->paintDevice();
199
0
    if (pdev) {
200
0
        if (image.isNull())
201
0
            image = getImage(doc, format);
202
0
        if (!image.isNull())
203
0
            scale = qreal(pdev->logicalDpiY()) / qreal(qt_defaultDpi());
204
0
    }
205
0
    size *= scale;
206
207
0
    return size;
208
0
}
209
210
QTextImageHandler::QTextImageHandler(QObject *parent)
211
0
    : QObject(parent)
212
0
{
213
0
}
214
215
QSizeF QTextImageHandler::intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format)
216
0
{
217
0
    Q_UNUSED(posInDocument)
218
0
    const QTextImageFormat imageFormat = format.toImageFormat();
219
220
0
    if (QCoreApplication::instance()->thread() != QThread::currentThread())
221
0
        return getImageSize(doc, imageFormat);
222
0
    return getPixmapSize(doc, imageFormat);
223
0
}
224
225
QImage QTextImageHandler::image(QTextDocument *doc, const QTextImageFormat &imageFormat)
226
0
{
227
0
    Q_ASSERT(doc != nullptr);
228
229
0
    return getImage(doc, imageFormat);
230
0
}
231
232
void QTextImageHandler::drawObject(QPainter *p, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format)
233
0
{
234
0
    Q_UNUSED(posInDocument)
235
0
        const QTextImageFormat imageFormat = format.toImageFormat();
236
237
0
    if (QCoreApplication::instance()->thread() != QThread::currentThread()) {
238
0
        const QImage image = getImage(doc, imageFormat, p->device()->devicePixelRatioF());
239
0
        p->drawImage(rect, image, image.rect());
240
0
    } else {
241
0
        const QPixmap pixmap = getPixmap(doc, imageFormat, p->device()->devicePixelRatioF());
242
0
        p->drawPixmap(rect, pixmap, pixmap.rect());
243
0
    }
244
0
}
245
246
QT_END_NAMESPACE
247
248
#include "moc_qtextimagehandler_p.cpp"