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:
parent
b3fcaea5f2
commit
7094466f7d
@ -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 },
|
||||
|
@ -189,6 +189,7 @@ enum Property {
|
||||
QtListNumberPrefix,
|
||||
QtListNumberSuffix,
|
||||
LineHeight,
|
||||
QtLineHeightType,
|
||||
NumProperties
|
||||
};
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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"))
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user