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 <lars.knoll@theqtcompany.com>
This commit is contained in:
Eskil Abrahamsen Blomfeldt 2016-03-30 13:15:02 +02:00
parent b3fcaea5f2
commit 7094466f7d
6 changed files with 134 additions and 18 deletions

View File

@ -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 },

View File

@ -189,6 +189,7 @@ enum Property {
QtListNumberPrefix,
QtListNumberSuffix,
LineHeight,
QtLineHeightType,
NumProperties
};

View File

@ -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());

View File

@ -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<QCss::Declaration>
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"))

View File

@ -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;

View File

@ -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()
"<p style=\"line-height: 40px\">Foo</p><p>Bar</p><p>Baz</p></body></html>");
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("<html><body>Foobar</body></html>");
QTextBlock block = td.begin();
QTextBlockFormat format = block.blockFormat();
QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::SingleHeight));
QCOMPARE(format.lineHeight(), 0.0);
}
{
QTextDocument td;
td.setHtml("<html><head><style type=\"text/css\">body { line-height: 40px; }</style></head><body>Foobar</body></html>");
QTextBlock block = td.begin();
QTextBlockFormat format = block.blockFormat();
QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::MinimumHeight));
QCOMPARE(format.lineHeight(), 40.0);
}
{
QTextDocument td;
td.setHtml("<html><head><style type=\"text/css\">body { line-height: 200%; }</style></head><body>Foobar</body></html>");
QTextBlock block = td.begin();
QTextBlockFormat format = block.blockFormat();
QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::ProportionalHeight));
QCOMPARE(format.lineHeight(), 200.0);
}
{
QTextDocument td;
td.setHtml("<html><head><style type=\"text/css\">body { line-height: 200%; -qt-line-height-type: single; }</style></head><body>Foobar</body></html>");
QTextBlock block = td.begin();
QTextBlockFormat format = block.blockFormat();
QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::SingleHeight));
QCOMPARE(format.lineHeight(), 200.0);
}
{
QTextDocument td;
td.setHtml("<html><head><style type=\"text/css\">body { line-height: 40px; -qt-line-height-type: proportional; }</style></head><body>Foobar</body></html>");
QTextBlock block = td.begin();
QTextBlockFormat format = block.blockFormat();
QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::ProportionalHeight));
QCOMPARE(format.lineHeight(), 40.0);
}
{
QTextDocument td;
td.setHtml("<html><head><style type=\"text/css\">body { line-height: 10; -qt-line-height-type: fixed; }</style></head><body>Foobar</body></html>");
QTextBlock block = td.begin();
QTextBlockFormat format = block.blockFormat();
QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::FixedHeight));
QCOMPARE(format.lineHeight(), 10.0);
}
{
QTextDocument td;
td.setHtml("<html><head><style type=\"text/css\">body { line-height: 33; -qt-line-height-type: minimum; }</style></head><body>Foobar</body></html>");
QTextBlock block = td.begin();
QTextBlockFormat format = block.blockFormat();
QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::MinimumHeight));
QCOMPARE(format.lineHeight(), 33.0);
}
{
QTextDocument td;
td.setHtml("<html><head><style type=\"text/css\">body { -qt-line-height-type: fixed; line-height: 200%; }</style></head><body>Foobar</body></html>");
QTextBlock block = td.begin();
QTextBlockFormat format = block.blockFormat();
QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::FixedHeight));
QCOMPARE(format.lineHeight(), 200.0);
}
{
QTextDocument td;
td.setHtml("<html><head><style type=\"text/css\">body { -qt-line-height-type: fixed; line-height: 200px; }</style></head><body>Foobar</body></html>");
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"