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
5#include "qtextimagehandler_p.h"
6
7#include <qguiapplication.h>
8#include <qtextformat.h>
9#include <qpainter.h>
10#include <qdebug.h>
11#include <qfile.h>
12#include <private/qtextengine_p.h>
13#include <qpalette.h>
14#include <qthread.h>
15
16QT_BEGIN_NAMESPACE
17
18using namespace Qt::StringLiterals;
19
20static inline QString findAtNxFileOrResource(const QString &baseFileName,
21 qreal targetDevicePixelRatio,
22 qreal *sourceDevicePixelRatio)
23{
24 // qt_findAtNxFile expects a file name that can be tested with QFile::exists.
25 // so if the format.name() is a file:/ or qrc:/ URL, then we need to strip away the schema.
26 QString localFile = baseFileName;
27 if (localFile.startsWith(s: "file:/"_L1))
28 localFile = localFile.sliced(pos: 6);
29 else if (localFile.startsWith(s: "qrc:/"_L1))
30 localFile = localFile.sliced(pos: 3);
31
32 extern QString qt_findAtNxFile(const QString &baseFileName, qreal targetDevicePixelRatio,
33 qreal *sourceDevicePixelRatio);
34 return qt_findAtNxFile(baseFileName: localFile, targetDevicePixelRatio, sourceDevicePixelRatio);
35}
36
37static inline QUrl fromLocalfileOrResources(QString path)
38{
39 if (path.startsWith(s: ":/"_L1)) // auto-detect resources and convert them to url
40 path = path.prepend(s: "qrc"_L1);
41 return QUrl(path);
42}
43
44template<typename T>
45static T getAs(QTextDocument *doc, const QTextImageFormat &format, const qreal devicePixelRatio = 1.0)
46{
47 qreal sourcePixelRatio = 1.0;
48 const QString name = findAtNxFileOrResource(baseFileName: format.name(), targetDevicePixelRatio: devicePixelRatio, sourceDevicePixelRatio: &sourcePixelRatio);
49 const QUrl url = fromLocalfileOrResources(path: name);
50
51 const QVariant data = doc->resource(type: QTextDocument::ImageResource, name: url);
52 T result;
53 if (data.userType() == QMetaType::QPixmap || data.userType() == QMetaType::QImage)
54 result = data.value<T>();
55 else if (data.metaType() == QMetaType::fromType<QByteArray>())
56 result.loadFromData(data.toByteArray());
57
58 if (result.isNull()) {
59 if (name.isEmpty() || !result.load(name))
60 return T(":/qt-project.org/styles/commonstyle/images/file-16.png"_L1);
61 doc->addResource(type: QTextDocument::ImageResource, name: url, resource: result);
62 }
63
64 if (sourcePixelRatio != 1.0)
65 result.setDevicePixelRatio(sourcePixelRatio);
66 return result;
67}
68
69template<typename T>
70static QSize getSize(QTextDocument *doc, const QTextImageFormat &format)
71{
72 const bool hasWidth = format.hasProperty(propertyId: QTextFormat::ImageWidth);
73 const int width = qRound(d: format.width());
74 const bool hasHeight = format.hasProperty(propertyId: QTextFormat::ImageHeight);
75 const int height = qRound(d: format.height());
76
77 T source;
78 QSize size(width, height);
79 if (!hasWidth || !hasHeight) {
80 source = getAs<T>(doc, format);
81 const QSizeF sourceSize = source.deviceIndependentSize();
82
83 if (!hasWidth) {
84 if (!hasHeight)
85 size.setWidth(sourceSize.width());
86 else
87 size.setWidth(qRound(d: height * (sourceSize.width() / qreal(sourceSize.height()))));
88 }
89 if (!hasHeight) {
90 if (!hasWidth)
91 size.setHeight(sourceSize.height());
92 else
93 size.setHeight(qRound(d: width * (sourceSize.height() / qreal(sourceSize.width()))));
94 }
95 }
96
97 qreal scale = 1.0;
98 QPaintDevice *pdev = doc->documentLayout()->paintDevice();
99 if (pdev) {
100 if (source.isNull())
101 source = getAs<T>(doc, format);
102 if (!source.isNull())
103 scale = qreal(pdev->logicalDpiY()) / qreal(qt_defaultDpi());
104 }
105 size *= scale;
106 return size;
107}
108
109QTextImageHandler::QTextImageHandler(QObject *parent)
110 : QObject(parent)
111{
112}
113
114QSizeF QTextImageHandler::intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format)
115{
116 Q_UNUSED(posInDocument);
117 const QTextImageFormat imageFormat = format.toImageFormat();
118
119 if (QCoreApplication::instance()->thread() != QThread::currentThread())
120 return getSize<QImage>(doc, format: imageFormat);
121 return getSize<QPixmap>(doc, format: imageFormat);
122}
123
124QImage QTextImageHandler::image(QTextDocument *doc, const QTextImageFormat &imageFormat)
125{
126 Q_ASSERT(doc != nullptr);
127
128 return getAs<QImage>(doc, format: imageFormat);
129}
130
131void QTextImageHandler::drawObject(QPainter *p, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format)
132{
133 Q_UNUSED(posInDocument);
134 const QTextImageFormat imageFormat = format.toImageFormat();
135
136 if (QCoreApplication::instance()->thread() != QThread::currentThread()) {
137 const QImage image = getAs<QImage>(doc, format: imageFormat, devicePixelRatio: p->device()->devicePixelRatio());
138 p->drawImage(targetRect: rect, image, sourceRect: image.rect());
139 } else {
140 const QPixmap pixmap = getAs<QPixmap>(doc, format: imageFormat, devicePixelRatio: p->device()->devicePixelRatio());
141 p->drawPixmap(targetRect: rect, pixmap, sourceRect: pixmap.rect());
142 }
143}
144
145QT_END_NAMESPACE
146
147#include "moc_qtextimagehandler_p.cpp"
148

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