diff options
-rw-r--r-- | src/gui/text/qcssparser.cpp | 12 | ||||
-rw-r--r-- | src/gui/text/qcssparser_p.h | 3 | ||||
-rw-r--r-- | src/gui/text/qtextdocument.cpp | 17 | ||||
-rw-r--r-- | src/gui/text/qtextformat.cpp | 21 | ||||
-rw-r--r-- | src/gui/text/qtextformat.h | 8 | ||||
-rw-r--r-- | src/gui/text/qtexthtmlparser.cpp | 7 | ||||
-rw-r--r-- | src/gui/text/qtextimagehandler.cpp | 26 | ||||
-rw-r--r-- | tests/auto/gui/text/qtextimagehandler/tst_qtextimagehandler.cpp | 66 |
8 files changed, 152 insertions, 8 deletions
diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp index e5815ffce2..916e96ea63 100644 --- a/src/gui/text/qcssparser.cpp +++ b/src/gui/text/qcssparser.cpp @@ -12,6 +12,7 @@ #include <qfontmetrics.h> #include <qbrush.h> #include <qimagereader.h> +#include <qtextformat.h> #include <algorithm> @@ -396,6 +397,8 @@ LengthData ValueExtractor::lengthValue(const Value& v) if (data.unit != LengthData::None) s.chop(2); + else if (v.type == Value::Percentage) + data.unit = LengthData::Percent; data.number = s.toDouble(); return data; @@ -409,6 +412,15 @@ static int lengthValueFromData(const LengthData& data, const QFont& f) return qRound(qBound(double(INT_MIN) + 0.1, scale * data.number, double(INT_MAX))); } +QTextLength ValueExtractor::textLength(const Declaration &decl) +{ + const LengthData data = lengthValue(decl.d->values.at(0)); + if (data.unit == LengthData::Percent) + return QTextLength(QTextLength::PercentageLength, data.number); + + return QTextLength(QTextLength::FixedLength, lengthValueFromData(data, f)); +} + int ValueExtractor::lengthValue(const Declaration &decl) { if (decl.d->parsed.isValid()) diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h index c1cfb1ac9b..7742271e41 100644 --- a/src/gui/text/qcssparser_p.h +++ b/src/gui/text/qcssparser_p.h @@ -392,7 +392,7 @@ QT_CSS_DECLARE_TYPEINFO(BackgroundData, Q_RELOCATABLE_TYPE) struct LengthData { qreal number; - enum { None, Px, Ex, Em } unit; + enum { None, Px, Ex, Em, Percent } unit; }; QT_CSS_DECLARE_TYPEINFO(LengthData, Q_PRIMITIVE_TYPE) @@ -835,6 +835,7 @@ struct Q_GUI_EXPORT ValueExtractor bool extractIcon(QIcon *icon, QSize *size); void lengthValues(const Declaration &decl, int *m); + QTextLength textLength(const Declaration &decl); private: void extractFont(); diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index 15a313e13d..ab788a5f9b 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -2944,6 +2944,17 @@ void QTextHtmlExporter::emitFragment(const QTextFragment &fragment) html += "<img"_L1; + QString maxWidthCss; + + if (imgFmt.hasProperty(QTextFormat::ImageMaxWidth)) { + auto length = imgFmt.lengthProperty(QTextFormat::ImageMaxWidth); + maxWidthCss += "max-width:"_L1; + if (length.type() == QTextLength::PercentageLength) + maxWidthCss += QString::number(length.rawValue()) + "%;"_L1; + else if (length.type() == QTextLength::FixedLength) + maxWidthCss += QString::number(length.rawValue()) + "px;"_L1; + } + if (imgFmt.hasProperty(QTextFormat::ImageName)) emitAttribute("src", imgFmt.name()); @@ -2960,9 +2971,11 @@ void QTextHtmlExporter::emitFragment(const QTextFragment &fragment) emitAttribute("height", QString::number(imgFmt.height())); if (imgFmt.verticalAlignment() == QTextCharFormat::AlignMiddle) - html += " style=\"vertical-align: middle;\""_L1; + html += " style=\"vertical-align: middle;"_L1 + maxWidthCss + u'\"'; else if (imgFmt.verticalAlignment() == QTextCharFormat::AlignTop) - html += " style=\"vertical-align: top;\""_L1; + html += " style=\"vertical-align: top;"_L1 + maxWidthCss + u'\"'; + else if (!maxWidthCss.isEmpty()) + html += " style=\""_L1 + maxWidthCss + u'\"'; if (QTextFrame *imageFrame = qobject_cast<QTextFrame *>(doc->objectForFormat(imgFmt))) emitFloatStyle(imageFrame->frameFormat().position()); diff --git a/src/gui/text/qtextformat.cpp b/src/gui/text/qtextformat.cpp index daa79a55d2..dacef70812 100644 --- a/src/gui/text/qtextformat.cpp +++ b/src/gui/text/qtextformat.cpp @@ -745,6 +745,7 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextTableCellFormat & \value ImageWidth \value ImageHeight \value ImageQuality + \value ImageMaxWidth This enum value has been added in Qt 6.8. Selection properties @@ -3426,7 +3427,7 @@ QTextImageFormat::QTextImageFormat(const QTextFormat &fmt) Sets the \a width of the rectangle occupied by the image. - \sa width(), setHeight() + \sa width(), setHeight(), maximumWidth() */ @@ -3438,6 +3439,24 @@ QTextImageFormat::QTextImageFormat(const QTextFormat &fmt) \sa height(), setWidth() */ +/*! + \fn void QTextImageFormat::setMaximumWidth(QTextLength maximumWidth) + + Sets the \a maximumWidth of the rectangle occupied by the image. This + can be an absolute number or a percentage of the available document size. + + \sa width(), setHeight() +*/ + + +/*! + \fn QTextLength QTextImageFormat::maximumWidth() const + + Returns the maximum width of the rectangle occupied by the image. + + \sa width(), setMaximumWidth() +*/ + /*! \fn void QTextImageFormat::setHeight(qreal height) diff --git a/src/gui/text/qtextformat.h b/src/gui/text/qtextformat.h index c009d328cb..2fa86ed0d1 100644 --- a/src/gui/text/qtextformat.h +++ b/src/gui/text/qtextformat.h @@ -241,6 +241,7 @@ public: ImageWidth = 0x5010, ImageHeight = 0x5011, ImageQuality = 0x5014, + ImageMaxWidth = 0x5015, // internal /* @@ -796,6 +797,10 @@ public: inline qreal width() const { return doubleProperty(ImageWidth); } + inline void setMaximumWidth(QTextLength maxWidth); + inline QTextLength maximumWidth() const + { return lengthProperty(ImageMaxWidth); } + inline void setHeight(qreal height); inline qreal height() const { return doubleProperty(ImageHeight); } @@ -823,6 +828,9 @@ inline void QTextImageFormat::setName(const QString &aname) inline void QTextImageFormat::setWidth(qreal awidth) { setProperty(ImageWidth, awidth); } +inline void QTextImageFormat::setMaximumWidth(QTextLength maxWidth) +{ setProperty(ImageMaxWidth, maxWidth); } + inline void QTextImageFormat::setHeight(qreal aheight) { setProperty(ImageHeight, aheight); } diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp index ee92cece78..bc2200697d 100644 --- a/src/gui/text/qtexthtmlparser.cpp +++ b/src/gui/text/qtexthtmlparser.cpp @@ -1428,6 +1428,13 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d charFormat.setForeground(brush); break; } + case QCss::MaximumWidth: + if (id == Html_img) { + auto imageFormat = charFormat.toImageFormat(); + imageFormat.setMaximumWidth(extractor.textLength(decl)); + charFormat = imageFormat; + } + break; default: break; } } diff --git a/src/gui/text/qtextimagehandler.cpp b/src/gui/text/qtextimagehandler.cpp index 5c56c30711..920e6c689c 100644 --- a/src/gui/text/qtextimagehandler.cpp +++ b/src/gui/text/qtextimagehandler.cpp @@ -12,6 +12,7 @@ #include <private/qtextengine_p.h> #include <qpalette.h> #include <qthread.h> +#include <limits> QT_BEGIN_NAMESPACE @@ -72,21 +73,40 @@ template<typename T> static QSize getSize(QTextDocument *doc, const QTextImageFormat &format) { const bool hasWidth = format.hasProperty(QTextFormat::ImageWidth); - const int width = qRound(format.width()); + 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<int>::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<T>(doc, format); - const QSizeF sourceSize = source.deviceIndependentSize(); + 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(qRound(height * (sourceSize.width() / qreal(sourceSize.height())))); + size.setWidth(qMin(effectiveMaxWidth, qRound(height * (sourceSize.width() / qreal(sourceSize.height()))))); } if (!hasHeight) { if (!hasWidth) diff --git a/tests/auto/gui/text/qtextimagehandler/tst_qtextimagehandler.cpp b/tests/auto/gui/text/qtextimagehandler/tst_qtextimagehandler.cpp index 8327a5d187..0048623d0e 100644 --- a/tests/auto/gui/text/qtextimagehandler/tst_qtextimagehandler.cpp +++ b/tests/auto/gui/text/qtextimagehandler/tst_qtextimagehandler.cpp @@ -6,6 +6,10 @@ #include <QPainter> #include <private/qtextimagehandler_p.h> +using namespace Qt::StringLiterals; + +// #define DEBUG_WRITE_HTML + class tst_QTextImageHandler : public QObject { Q_OBJECT @@ -20,6 +24,8 @@ private slots: void loadAtNImages_data(); #ifndef QT_NO_TEXTHTMLPARSER void loadAtNImages(); + void maxWidth_data(); + void maxWidth(); #endif }; @@ -61,7 +67,7 @@ void tst_QTextImageHandler::loadAtNImages() const auto it = std::find_if(formats.begin(), formats.end(), [](const auto &format){ return format.objectType() == QTextFormat::ImageObject; }); - QVERIFY(it != formats.end()); + QCOMPARE_NE(it, formats.end()); const QTextImageFormat format = (*it).toImageFormat(); QTextImageHandler handler; @@ -77,6 +83,64 @@ void tst_QTextImageHandler::loadAtNImages() QCOMPARE(img.pixelColor(0, 0), expectedColor); } } + +void tst_QTextImageHandler::maxWidth_data() +{ + QTest::addColumn<QString>("imageFile"); + QTest::addColumn<QSizeF>("pageSize"); + QTest::addColumn<QTextLength>("maxWidth"); + QTest::addColumn<QSizeF>("expectedSize"); + + QTest::addRow("constrained-percentage") << QFINDTESTDATA("data/image.png") << QSizeF(16, 16) << QTextLength(QTextLength::PercentageLength, 100) << QSizeF(12, 12); + QTest::addRow("not-constrained-percentage") << QFINDTESTDATA("data/image.png") << QSizeF(200, 200) << QTextLength(QTextLength::PercentageLength, 100) << QSizeF(16, 16); + QTest::addRow("constrained-fixed") << QFINDTESTDATA("data/image.png") << QSizeF(16, 16) << QTextLength(QTextLength::FixedLength, 5) << QSizeF(5, 5); + QTest::addRow("not-constrained-fixed") << QFINDTESTDATA("data/image.png") << QSizeF(200, 200) << QTextLength(QTextLength::FixedLength, 5) << QSizeF(5, 5); + QTest::addRow("not-constrained-default") << QFINDTESTDATA("data/image.png") << QSizeF(200, 200) << QTextLength(QTextLength::VariableLength, 5) << QSizeF(16, 16); +} + +void tst_QTextImageHandler::maxWidth() +{ + QFETCH(QString, imageFile); + QFETCH(QSizeF, pageSize); + QFETCH(QTextLength, maxWidth); + QFETCH(QSizeF, expectedSize); + + QTextDocument doc; + doc.setPageSize(pageSize); + doc.setDocumentMargin(2); + QTextCursor c(&doc); + QString style; + if (maxWidth.type() == QTextLength::PercentageLength) + style = " style=\"max-width:"_L1 + QString::number(maxWidth.rawValue()) + "%;\""_L1; + else if (maxWidth.type() == QTextLength::FixedLength) + style = " style=\"max-width:"_L1 + QString::number(maxWidth.rawValue()) + "px;\""_L1; + const QString html = "<img src=\"" + imageFile + u'\"' + style + "\">"; + c.insertHtml(html); + +#ifdef DEBUG_WRITE_HTML + { + QFile out("/tmp/" + QLatin1String(QTest::currentDataTag()) + ".html"); + out.open(QFile::WriteOnly); + out.write(html.toLatin1()); + out.close(); + } + { + QFile out("/tmp/" + QLatin1String(QTest::currentDataTag()) + "_rewrite.html"); + out.open(QFile::WriteOnly); + out.write(doc.toHtml().toLatin1()); + out.close(); + } +#endif + const auto formats = doc.allFormats(); + const auto it = std::find_if(formats.begin(), formats.end(), [](const auto &format){ + return format.objectType() == QTextFormat::ImageObject; + }); + QCOMPARE_NE(it, formats.end()); + const QTextImageFormat format = (*it).toImageFormat(); + QTextImageHandler handler; + + QCOMPARE(handler.intrinsicSize(&doc, 0, format), expectedSize); +} #endif QTEST_MAIN(tst_QTextImageHandler) |