// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qtextimagehandler_p.h" #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; static inline QString findAtNxFileOrResource(const QString &baseFileName, qreal targetDevicePixelRatio, qreal *sourceDevicePixelRatio) { // qt_findAtNxFile expects a file name that can be tested with QFile::exists. // so if the format.name() is a file:/ or qrc:/ URL, then we need to strip away the schema. QString localFile; const QUrl url(baseFileName); if (url.isLocalFile()) localFile = url.toLocalFile(); else if (baseFileName.startsWith("qrc:/"_L1)) localFile = baseFileName.sliced(3); else localFile = baseFileName; extern QString qt_findAtNxFile(const QString &baseFileName, qreal targetDevicePixelRatio, qreal *sourceDevicePixelRatio); return qt_findAtNxFile(localFile, targetDevicePixelRatio, sourceDevicePixelRatio); } static inline QUrl fromLocalfileOrResources(QString path) { if (path.startsWith(":/"_L1)) // auto-detect resources and convert them to url path = path.prepend("qrc"_L1); return QUrl(path); } template static T getAs(QTextDocument *doc, const QTextImageFormat &format, const qreal devicePixelRatio = 1.0) { qreal sourcePixelRatio = 1.0; const QString name = findAtNxFileOrResource(format.name(), devicePixelRatio, &sourcePixelRatio); const QUrl url = fromLocalfileOrResources(name); const QVariant data = doc->resource(QTextDocument::ImageResource, url); T result; if (data.userType() == QMetaType::QPixmap || data.userType() == QMetaType::QImage) result = data.value(); else if (data.metaType() == QMetaType::fromType()) result.loadFromData(data.toByteArray()); if (result.isNull()) { if (name.isEmpty() || !result.load(name)) return T(":/qt-project.org/styles/commonstyle/images/file-16.png"_L1); doc->addResource(QTextDocument::ImageResource, url, result); } if (sourcePixelRatio != 1.0) result.setDevicePixelRatio(sourcePixelRatio); return result; } template static QSize getSize(QTextDocument *doc, const QTextImageFormat &format) { const bool hasWidth = format.hasProperty(QTextFormat::ImageWidth); int width = qRound(format.width()); const bool hasHeight = format.hasProperty(QTextFormat::ImageHeight); const int height = qRound(format.height()); const bool hasMaxWidth = format.hasProperty(QTextFormat::ImageMaxWidth); const auto maxWidth = format.maximumWidth(); int effectiveMaxWidth = std::numeric_limits::max(); if (hasMaxWidth) { if (maxWidth.type() == QTextLength::PercentageLength) effectiveMaxWidth = (doc->pageSize().width() - 2 * doc->documentMargin()) * maxWidth.value(100) / 100; else effectiveMaxWidth = maxWidth.rawValue(); width = qMin(effectiveMaxWidth, width); } T source; QSize size(width, height); if (!hasWidth || !hasHeight) { source = getAs(doc, format); QSizeF sourceSize = source.deviceIndependentSize(); if (sourceSize.width() > effectiveMaxWidth) { // image is bigger than effectiveMaxWidth, scale it down sourceSize.setHeight(effectiveMaxWidth * (sourceSize.height() / qreal(sourceSize.width()))); sourceSize.setWidth(effectiveMaxWidth); } if (!hasWidth) { if (!hasHeight) size.setWidth(sourceSize.width()); else size.setWidth(qMin(effectiveMaxWidth, qRound(height * (sourceSize.width() / qreal(sourceSize.height()))))); } if (!hasHeight) { if (!hasWidth) size.setHeight(sourceSize.height()); else size.setHeight(qRound(width * (sourceSize.height() / qreal(sourceSize.width())))); } } qreal scale = 1.0; QPaintDevice *pdev = doc->documentLayout()->paintDevice(); if (pdev) { if (source.isNull()) source = getAs(doc, format); if (!source.isNull()) scale = qreal(pdev->logicalDpiY()) / qreal(qt_defaultDpi()); } size *= scale; return size; } QTextImageHandler::QTextImageHandler(QObject *parent) : QObject(parent) { } QSizeF QTextImageHandler::intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format) { Q_UNUSED(posInDocument); const QTextImageFormat imageFormat = format.toImageFormat(); if (QCoreApplication::instance()->thread() != QThread::currentThread()) return getSize(doc, imageFormat); return getSize(doc, imageFormat); } QImage QTextImageHandler::image(QTextDocument *doc, const QTextImageFormat &imageFormat) { Q_ASSERT(doc != nullptr); return getAs(doc, imageFormat); } void QTextImageHandler::drawObject(QPainter *p, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format) { Q_UNUSED(posInDocument); const QTextImageFormat imageFormat = format.toImageFormat(); if (QCoreApplication::instance()->thread() != QThread::currentThread()) { const QImage image = getAs(doc, imageFormat, p->device()->devicePixelRatio()); p->drawImage(rect, image, image.rect()); } else { const QPixmap pixmap = getAs(doc, imageFormat, p->device()->devicePixelRatio()); p->drawPixmap(rect, pixmap, pixmap.rect()); } } QT_END_NAMESPACE #include "moc_qtextimagehandler_p.cpp"