From 7094466f7d0c2176eb3080021a4ea5d220555df9 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Wed, 30 Mar 2016 13:15:02 +0200 Subject: Interpret fixed CSS line-height as minimum rather than absolute The QTextBlockFormat::FixedHeight overrides the line height regardless of its calculated height. If the line contains objects or text which is higher than the specified line height, using FixedHeight will cause them to overlap with the previous line. This is not what happens in normal web browsers. The expected behavior is that the line height given in CSS is the minimum height, but that we still reserve space needed to display everything without overlaps. To make it possible for people to retain the old behavior, we introduce the -qt-line-height-type property, which allows them to override the default. This also fixes output from toHtml() to use the new property rather than set the minimum height of the paragraph or the "line-spacing" property, which does not exist in either CSS nor in Qt. [ChangeLog][QtGui][Important Behavior Changes] When line height is specified in pixels, this is now interpreted as the minimum line height rather than an absolute line height to avoid overlaps. To get the old behavior, use the -qt-line-height-type property in CSS and set it to "fixed". Task-number: QTBUG-51962 Change-Id: Ic2dde649b69209672170dad4c2de1e1c432a1078 Reviewed-by: Lars Knoll --- src/gui/text/qcssparser.cpp | 1 + src/gui/text/qcssparser_p.h | 1 + src/gui/text/qtextdocument.cpp | 17 ++--- src/gui/text/qtexthtmlparser.cpp | 44 +++++++++-- src/gui/text/qtexthtmlparser_p.h | 1 + .../gui/text/qtextdocument/tst_qtextdocument.cpp | 88 +++++++++++++++++++++- 6 files changed, 134 insertions(+), 18 deletions(-) diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp index b9e05e726e..53d4e3fa36 100644 --- a/src/gui/text/qcssparser.cpp +++ b/src/gui/text/qcssparser.cpp @@ -67,6 +67,7 @@ struct QCssKnownValue static const QCssKnownValue properties[NumProperties - 1] = { { "-qt-background-role", QtBackgroundRole }, { "-qt-block-indent", QtBlockIndent }, + { "-qt-line-height-type", QtLineHeightType }, { "-qt-list-indent", QtListIndent }, { "-qt-list-number-prefix", QtListNumberPrefix }, { "-qt-list-number-suffix", QtListNumberSuffix }, diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h index b7692d32aa..4da7b62dba 100644 --- a/src/gui/text/qcssparser_p.h +++ b/src/gui/text/qcssparser_p.h @@ -189,6 +189,7 @@ enum Property { QtListNumberPrefix, QtListNumberSuffix, LineHeight, + QtLineHeightType, NumProperties }; diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index f50f771c07..febd5da488 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -2763,26 +2763,25 @@ void QTextHtmlExporter::emitBlockAttributes(const QTextBlock &block) } if (format.lineHeightType() != QTextBlockFormat::SingleHeight) { + html += QLatin1String(" line-height:") + + QString::number(format.lineHeight()); switch (format.lineHeightType()) { case QTextBlockFormat::ProportionalHeight: + html += QLatin1String("%;"); + break; case QTextBlockFormat::FixedHeight: - html += QLatin1String(" line-height:"); + html += QLatin1String("; -qt-line-height-type: fixed;"); break; case QTextBlockFormat::MinimumHeight: - html += QLatin1String(" min-height:"); + html += QLatin1String("px;"); break; case QTextBlockFormat::LineDistanceHeight: - html += QLatin1String(" line-spacing:"); + html += QLatin1String("; -qt-line-height-type: line-distance;"); break; - case QTextBlockFormat::SingleHeight: default: + html += QLatin1String(";"); break; // Should never reach here } - html += QString::number(format.lineHeight()); - if (format.lineHeightType() == QTextBlockFormat::ProportionalHeight) - html += QLatin1String("%;"); - else - html += QLatin1String("px;"); } emitPageBreakPolicy(format.pageBreakPolicy()); diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp index d8e12f7024..576ff7d935 100644 --- a/src/gui/text/qtexthtmlparser.cpp +++ b/src/gui/text/qtexthtmlparser.cpp @@ -492,7 +492,7 @@ static QString quoteNewline(const QString &s) QTextHtmlParserNode::QTextHtmlParserNode() : parent(0), id(Html_unknown), - cssFloat(QTextFrameFormat::InFlow), hasOwnListStyle(false), + cssFloat(QTextFrameFormat::InFlow), hasOwnListStyle(false), hasOwnLineHeightType(false), hasCssListIndent(false), isEmptyParagraph(false), isTextFrame(false), isRootFrame(false), displayMode(QTextHtmlElement::DisplayInline), hasHref(false), listStyle(QTextListFormat::ListStyleUndefined), imageWidth(-1), imageHeight(-1), tableBorder(0), @@ -1198,20 +1198,48 @@ void QTextHtmlParserNode::applyCssDeclarations(const QVector case QCss::QtBlockIndent: blockFormat.setIndent(decl.d->values.first().variant.toInt()); break; - case QCss::LineHeight: { + case QCss::QtLineHeightType: { + QString lineHeightTypeName = decl.d->values.first().variant.toString(); + QTextBlockFormat::LineHeightTypes lineHeightType; + if (lineHeightTypeName.compare(QLatin1String("proportional"), Qt::CaseInsensitive) == 0) + lineHeightType = QTextBlockFormat::ProportionalHeight; + else if (lineHeightTypeName.compare(QLatin1String("fixed"), Qt::CaseInsensitive) == 0) + lineHeightType = QTextBlockFormat::FixedHeight; + else if (lineHeightTypeName.compare(QLatin1String("minimum"), Qt::CaseInsensitive) == 0) + lineHeightType = QTextBlockFormat::MinimumHeight; + else if (lineHeightTypeName.compare(QLatin1String("line-distance"), Qt::CaseInsensitive) == 0) + lineHeightType = QTextBlockFormat::LineDistanceHeight; + else + lineHeightType = QTextBlockFormat::SingleHeight; + + blockFormat.setProperty(QTextBlockFormat::LineHeightType, lineHeightType); + hasOwnLineHeightType = true; + } + break; + case QCss::LineHeight: { qreal lineHeight; + QTextBlockFormat::LineHeightTypes lineHeightType; if (decl.realValue(&lineHeight, "px")) { - blockFormat.setLineHeight(lineHeight, QTextBlockFormat::FixedHeight); + lineHeightType = QTextBlockFormat::MinimumHeight; } else { bool ok; QString value = decl.d->values.first().toString(); lineHeight = value.toDouble(&ok); - if (ok) - blockFormat.setLineHeight(lineHeight, QTextBlockFormat::ProportionalHeight); - else - blockFormat.setLineHeight(0, QTextBlockFormat::SingleHeight); + if (ok) { + lineHeightType = QTextBlockFormat::ProportionalHeight; + } else { + lineHeight = 0.0; + lineHeightType = QTextBlockFormat::SingleHeight; + } } - break; } + + // Only override line height type if specified in same node + if (hasOwnLineHeightType) + lineHeightType = QTextBlockFormat::LineHeightTypes(blockFormat.lineHeightType()); + + blockFormat.setLineHeight(lineHeight, lineHeightType); + break; + } case QCss::TextIndent: { qreal indent = 0; if (decl.realValue(&indent, "px")) diff --git a/src/gui/text/qtexthtmlparser_p.h b/src/gui/text/qtexthtmlparser_p.h index 8e5a90be0b..4c0dd967f9 100644 --- a/src/gui/text/qtexthtmlparser_p.h +++ b/src/gui/text/qtexthtmlparser_p.h @@ -171,6 +171,7 @@ struct QTextHtmlParserNode { QTextBlockFormat blockFormat; uint cssFloat : 2; uint hasOwnListStyle : 1; + uint hasOwnLineHeightType : 1; uint hasCssListIndent : 1; uint isEmptyParagraph : 1; uint isTextFrame : 1; diff --git a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp index 04e6bba91e..1869139a98 100644 --- a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp +++ b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp @@ -183,6 +183,8 @@ private slots: void textCursorUsageWithinContentsChange(); void cssInheritance(); + + void lineHeightType(); private: void backgroundImage_checkExpectedHtml(const QTextDocument &doc); void buildRegExpData(); @@ -3191,7 +3193,7 @@ void tst_QTextDocument::cssInheritance() "

Foo

Bar

Baz

"); QTextBlock block = td.begin(); QTextBlockFormat fmt = block.blockFormat(); - QCOMPARE(fmt.lineHeightType(), int(QTextBlockFormat::FixedHeight)); + QCOMPARE(fmt.lineHeightType(), int(QTextBlockFormat::MinimumHeight)); QCOMPARE(fmt.lineHeight(), qreal(40)); block = block.next(); fmt = block.blockFormat(); @@ -3276,5 +3278,89 @@ void tst_QTextDocument::cssInheritance() } } +void tst_QTextDocument::lineHeightType() +{ + { + QTextDocument td; + td.setHtml("Foobar"); + QTextBlock block = td.begin(); + QTextBlockFormat format = block.blockFormat(); + QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::SingleHeight)); + QCOMPARE(format.lineHeight(), 0.0); + } + + { + QTextDocument td; + td.setHtml("Foobar"); + QTextBlock block = td.begin(); + QTextBlockFormat format = block.blockFormat(); + QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::MinimumHeight)); + QCOMPARE(format.lineHeight(), 40.0); + } + + { + QTextDocument td; + td.setHtml("Foobar"); + QTextBlock block = td.begin(); + QTextBlockFormat format = block.blockFormat(); + QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::ProportionalHeight)); + QCOMPARE(format.lineHeight(), 200.0); + } + + { + QTextDocument td; + td.setHtml("Foobar"); + QTextBlock block = td.begin(); + QTextBlockFormat format = block.blockFormat(); + QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::SingleHeight)); + QCOMPARE(format.lineHeight(), 200.0); + } + + { + QTextDocument td; + td.setHtml("Foobar"); + QTextBlock block = td.begin(); + QTextBlockFormat format = block.blockFormat(); + QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::ProportionalHeight)); + QCOMPARE(format.lineHeight(), 40.0); + } + + { + QTextDocument td; + td.setHtml("Foobar"); + QTextBlock block = td.begin(); + QTextBlockFormat format = block.blockFormat(); + QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::FixedHeight)); + QCOMPARE(format.lineHeight(), 10.0); + } + + { + QTextDocument td; + td.setHtml("Foobar"); + QTextBlock block = td.begin(); + QTextBlockFormat format = block.blockFormat(); + QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::MinimumHeight)); + QCOMPARE(format.lineHeight(), 33.0); + } + + { + QTextDocument td; + td.setHtml("Foobar"); + QTextBlock block = td.begin(); + QTextBlockFormat format = block.blockFormat(); + QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::FixedHeight)); + QCOMPARE(format.lineHeight(), 200.0); + } + + { + QTextDocument td; + td.setHtml("Foobar"); + QTextBlock block = td.begin(); + QTextBlockFormat format = block.blockFormat(); + QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::FixedHeight)); + QCOMPARE(format.lineHeight(), 200.0); + } +} + QTEST_MAIN(tst_QTextDocument) #include "tst_qtextdocument.moc" -- cgit v1.2.3