From d8a9bec35fb0c60a0e5990c1a12ffe6f39fdbf2d Mon Sep 17 00:00:00 2001 From: Nils Jeisecke Date: Thu, 17 Dec 2015 16:38:15 +0100 Subject: [PATCH] QTextDocument: add css-styling of table cell borders to HTML import/export Supported style attributes: style: supports "border-collapse: collapse" and "border-color". border: width of the outer border bordercolor: basic color for all borders style: not supported
/ style: supports the "border", "border-[top|left|bottom|right]]" shorthand styles and the "border-width", "border-color" and "border-style" (and the top/left/bottom/right variants) attributes will render a simple 1px table grid. Notes: The QTextDocument table model is much simpler than the HTML table model. It basically only has
and and
support. So the HTML parser is forced to map markup and styling to the QTextDocument model which is not without loss. In other words: While QTextDocument -> HTML -> QTextDocument should preserve the QTextDocument structure, HTML -> QTextDocument -> HTML does not preserve the HTML DOM at all. So for now the HTML importer and writer only support border styles on the and nodes. In future updates, the HTML parser might be enhanced to map
CSS styles to the cells. Change-Id: If9e7312fa6cbf270cf8f7b3c72ba1fa094107517 Reviewed-by: Shawn Rutledge --- src/gui/text/qcssparser.cpp | 9 ++ src/gui/text/qcssparser_p.h | 2 + src/gui/text/qtextdocument.cpp | 102 +++++++------ src/gui/text/qtextdocumentfragment.cpp | 26 ++++ src/gui/text/qtexthtmlparser.cpp | 34 +++++ src/gui/text/qtexthtmlparser_p.h | 8 ++ .../text/qtextdocument/tst_qtextdocument.cpp | 29 ++++ .../tst_qtextdocumentfragment.cpp | 134 ++++++++++++++++++ .../gui/text/qtexttable/tst_qtexttable.cpp | 108 ++++++++++++++ 9 files changed, 412 insertions(+), 40 deletions(-) diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp index 45f1ca596ec..0b2dedf5dc6 100644 --- a/src/gui/text/qcssparser.cpp +++ b/src/gui/text/qcssparser.cpp @@ -92,6 +92,7 @@ static const QCssKnownValue properties[NumProperties - 1] = { { "border-bottom-right-radius", BorderBottomRightRadius }, { "border-bottom-style", BorderBottomStyle }, { "border-bottom-width", BorderBottomWidth }, + { "border-collapse", BorderCollapse }, { "border-color", BorderColor }, { "border-image", BorderImage }, { "border-left", BorderLeft }, @@ -1732,6 +1733,14 @@ void Declaration::borderImageValue(QString *image, int *cuts, *h = *v; } +bool Declaration::borderCollapseValue() const +{ + if (d->values.count() != 1) + return false; + else + return d->values.at(0).toString() == QLatin1String("collapse"); +} + QIcon Declaration::iconValue() const { if (d->parsed.isValid()) diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h index ddc46803aeb..ab85e76cf3a 100644 --- a/src/gui/text/qcssparser_p.h +++ b/src/gui/text/qcssparser_p.h @@ -122,6 +122,7 @@ enum Property { BorderRight, BorderTop, BorderBottom, + BorderCollapse, Padding, PaddingLeft, PaddingRight, @@ -478,6 +479,7 @@ struct Q_GUI_EXPORT Declaration QIcon iconValue() const; void borderImageValue(QString *image, int *cuts, TileMode *h, TileMode *v) const; + bool borderCollapseValue() const; }; QT_CSS_DECLARE_TYPEINFO(Declaration, Q_MOVABLE_TYPE) diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index dc34a96918f..c80617f929a 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -2593,51 +2593,43 @@ void QTextHtmlExporter::emitFloatStyle(QTextFrameFormat::Position pos, StyleMode html += QLatin1Char('\"'); } +static QLatin1String richtextBorderStyleToHtmlBorderStyle(QTextFrameFormat::BorderStyle style) +{ + switch (style) { + case QTextFrameFormat::BorderStyle_None: + return QLatin1String("none"); + case QTextFrameFormat::BorderStyle_Dotted: + return QLatin1String("dotted"); + case QTextFrameFormat::BorderStyle_Dashed: + return QLatin1String("dashed"); + case QTextFrameFormat::BorderStyle_Solid: + return QLatin1String("solid"); + case QTextFrameFormat::BorderStyle_Double: + return QLatin1String("double"); + case QTextFrameFormat::BorderStyle_DotDash: + return QLatin1String("dot-dash"); + case QTextFrameFormat::BorderStyle_DotDotDash: + return QLatin1String("dot-dot-dash"); + case QTextFrameFormat::BorderStyle_Groove: + return QLatin1String("groove"); + case QTextFrameFormat::BorderStyle_Ridge: + return QLatin1String("ridge"); + case QTextFrameFormat::BorderStyle_Inset: + return QLatin1String("inset"); + case QTextFrameFormat::BorderStyle_Outset: + return QLatin1String("outset"); + default: + Q_UNREACHABLE(); + }; + return QLatin1String(""); +} + void QTextHtmlExporter::emitBorderStyle(QTextFrameFormat::BorderStyle style) { Q_ASSERT(style <= QTextFrameFormat::BorderStyle_Outset); html += QLatin1String(" border-style:"); - - switch (style) { - case QTextFrameFormat::BorderStyle_None: - html += QLatin1String("none"); - break; - case QTextFrameFormat::BorderStyle_Dotted: - html += QLatin1String("dotted"); - break; - case QTextFrameFormat::BorderStyle_Dashed: - html += QLatin1String("dashed"); - break; - case QTextFrameFormat::BorderStyle_Solid: - html += QLatin1String("solid"); - break; - case QTextFrameFormat::BorderStyle_Double: - html += QLatin1String("double"); - break; - case QTextFrameFormat::BorderStyle_DotDash: - html += QLatin1String("dot-dash"); - break; - case QTextFrameFormat::BorderStyle_DotDotDash: - html += QLatin1String("dot-dot-dash"); - break; - case QTextFrameFormat::BorderStyle_Groove: - html += QLatin1String("groove"); - break; - case QTextFrameFormat::BorderStyle_Ridge: - html += QLatin1String("ridge"); - break; - case QTextFrameFormat::BorderStyle_Inset: - html += QLatin1String("inset"); - break; - case QTextFrameFormat::BorderStyle_Outset: - html += QLatin1String("outset"); - break; - default: - Q_ASSERT(false); - break; - }; - + html += richtextBorderStyleToHtmlBorderStyle(style); html += QLatin1Char(';'); } @@ -3204,6 +3196,33 @@ void QTextHtmlExporter::emitTable(const QTextTable *table) if (cellFormat.hasProperty(QTextFormat::TableCellBottomPadding)) styleString += QLatin1String(" padding-bottom:") + QString::number(cellFormat.bottomPadding()) + QLatin1Char(';'); + if (cellFormat.hasProperty(QTextFormat::TableCellTopBorder)) + styleString += QLatin1String(" border-top:") + QString::number(cellFormat.topBorder()) + QLatin1String("px;"); + if (cellFormat.hasProperty(QTextFormat::TableCellRightBorder)) + styleString += QLatin1String(" border-right:") + QString::number(cellFormat.rightBorder()) + QLatin1String("px;"); + if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorder)) + styleString += QLatin1String(" border-bottom:") + QString::number(cellFormat.bottomBorder()) + QLatin1String("px;"); + if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorder)) + styleString += QLatin1String(" border-left:") + QString::number(cellFormat.leftBorder()) + QLatin1String("px;"); + + if (cellFormat.hasProperty(QTextFormat::TableCellTopBorderBrush)) + styleString += QLatin1String(" border-top-color:") + cellFormat.topBorderBrush().color().name() + QLatin1Char(';'); + if (cellFormat.hasProperty(QTextFormat::TableCellRightBorderBrush)) + styleString += QLatin1String(" border-right-color:") + cellFormat.rightBorderBrush().color().name() + QLatin1Char(';'); + if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorderBrush)) + styleString += QLatin1String(" border-bottom-color:") + cellFormat.bottomBorderBrush().color().name() + QLatin1Char(';'); + if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorderBrush)) + styleString += QLatin1String(" border-left-color:") + cellFormat.leftBorderBrush().color().name() + QLatin1Char(';'); + + if (cellFormat.hasProperty(QTextFormat::TableCellTopBorderStyle)) + styleString += QLatin1String(" border-top-style:") + richtextBorderStyleToHtmlBorderStyle(cellFormat.topBorderStyle()) + QLatin1Char(';'); + if (cellFormat.hasProperty(QTextFormat::TableCellRightBorderStyle)) + styleString += QLatin1String(" border-right-style:") + richtextBorderStyleToHtmlBorderStyle(cellFormat.rightBorderStyle()) + QLatin1Char(';'); + if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorderStyle)) + styleString += QLatin1String(" border-bottom-style:") + richtextBorderStyleToHtmlBorderStyle(cellFormat.bottomBorderStyle()) + QLatin1Char(';'); + if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorderStyle)) + styleString += QLatin1String(" border-left-style:") + richtextBorderStyleToHtmlBorderStyle(cellFormat.leftBorderStyle()) + QLatin1Char(';'); + if (!styleString.isEmpty()) html += QLatin1String(" style=\"") + styleString + QLatin1Char('\"'); @@ -3310,6 +3329,9 @@ void QTextHtmlExporter::emitFrameStyle(const QTextFrameFormat &format, FrameType QString::number(format.leftMargin()), QString::number(format.rightMargin())); + if (format.property(QTextFormat::TableBorderCollapse).toBool()) + html += QLatin1String(" border-collapse:collapse;"); + if (html.length() == originalHtmlLength) // nothing emitted? html.chop(styleAttribute.size()); else diff --git a/src/gui/text/qtextdocumentfragment.cpp b/src/gui/text/qtextdocumentfragment.cpp index 8ad1300e6cf..778493d4bce 100644 --- a/src/gui/text/qtextdocumentfragment.cpp +++ b/src/gui/text/qtextdocumentfragment.cpp @@ -986,6 +986,7 @@ QTextHtmlImporter::Table QTextHtmlImporter::scanTable(int tableNodeIdx) tableFmt.setColumns(table.columns); tableFmt.setColumnWidthConstraints(columnWidths); tableFmt.setHeaderRowCount(tableHeaderRowCount); + tableFmt.setBorderCollapse(node.borderCollapse); fmt = tableFmt; } @@ -1061,6 +1062,31 @@ QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processBlockNode() fmt.setLeftPadding(leftPadding(currentNodeIdx)); if (rightPadding(currentNodeIdx) >= 0) fmt.setRightPadding(rightPadding(currentNodeIdx)); + if (tableCellBorder(currentNodeIdx, QCss::TopEdge) > 0) + fmt.setTopBorder(tableCellBorder(currentNodeIdx, QCss::TopEdge)); + if (tableCellBorder(currentNodeIdx, QCss::RightEdge) > 0) + fmt.setRightBorder(tableCellBorder(currentNodeIdx, QCss::RightEdge)); + if (tableCellBorder(currentNodeIdx, QCss::BottomEdge) > 0) + fmt.setBottomBorder(tableCellBorder(currentNodeIdx, QCss::BottomEdge)); + if (tableCellBorder(currentNodeIdx, QCss::LeftEdge) > 0) + fmt.setLeftBorder(tableCellBorder(currentNodeIdx, QCss::LeftEdge)); + if (tableCellBorderStyle(currentNodeIdx, QCss::TopEdge) != QTextFrameFormat::BorderStyle_None) + fmt.setTopBorderStyle(tableCellBorderStyle(currentNodeIdx, QCss::TopEdge)); + if (tableCellBorderStyle(currentNodeIdx, QCss::RightEdge) != QTextFrameFormat::BorderStyle_None) + fmt.setRightBorderStyle(tableCellBorderStyle(currentNodeIdx, QCss::RightEdge)); + if (tableCellBorderStyle(currentNodeIdx, QCss::BottomEdge) != QTextFrameFormat::BorderStyle_None) + fmt.setBottomBorderStyle(tableCellBorderStyle(currentNodeIdx, QCss::BottomEdge)); + if (tableCellBorderStyle(currentNodeIdx, QCss::LeftEdge) != QTextFrameFormat::BorderStyle_None) + fmt.setLeftBorderStyle(tableCellBorderStyle(currentNodeIdx, QCss::LeftEdge)); + if (tableCellBorderBrush(currentNodeIdx, QCss::TopEdge) != Qt::NoBrush) + fmt.setTopBorderBrush(tableCellBorderBrush(currentNodeIdx, QCss::TopEdge)); + if (tableCellBorderBrush(currentNodeIdx, QCss::RightEdge) != Qt::NoBrush) + fmt.setRightBorderBrush(tableCellBorderBrush(currentNodeIdx, QCss::RightEdge)); + if (tableCellBorderBrush(currentNodeIdx, QCss::BottomEdge) != Qt::NoBrush) + fmt.setBottomBorderBrush(tableCellBorderBrush(currentNodeIdx, QCss::BottomEdge)); + if (tableCellBorderBrush(currentNodeIdx, QCss::LeftEdge) != Qt::NoBrush) + fmt.setLeftBorderBrush(tableCellBorderBrush(currentNodeIdx, QCss::LeftEdge)); + cell.setFormat(fmt); cursor.setPosition(cell.firstPosition()); diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp index 43b32e7e2c0..5d37982a8bd 100644 --- a/src/gui/text/qtexthtmlparser.cpp +++ b/src/gui/text/qtexthtmlparser.cpp @@ -491,12 +491,19 @@ QTextHtmlParserNode::QTextHtmlParserNode() listStyle(QTextListFormat::ListStyleUndefined), imageWidth(-1), imageHeight(-1), tableBorder(0), tableCellRowSpan(1), tableCellColSpan(1), tableCellSpacing(2), tableCellPadding(0), borderBrush(Qt::darkGray), borderStyle(QTextFrameFormat::BorderStyle_Outset), + borderCollapse(false), userState(-1), cssListIndent(0), wsm(WhiteSpaceModeUndefined) { margin[QTextHtmlParser::MarginLeft] = 0; margin[QTextHtmlParser::MarginRight] = 0; margin[QTextHtmlParser::MarginTop] = 0; margin[QTextHtmlParser::MarginBottom] = 0; + + for (int i = 0; i < 4; ++i) { + tableCellBorderStyle[i] = QTextFrameFormat::BorderStyle_None; + tableCellBorder[i] = 0; + tableCellBorderBrush[i] = Qt::NoBrush; + } } void QTextHtmlParser::dumpHtml() @@ -1169,6 +1176,25 @@ void QTextHtmlParserNode::applyCssDeclarations(const QVector QCss::ValueExtractor extractor(declarations); extractor.extractBox(margin, padding); + if (id == Html_td || id == Html_th) { + QCss::BorderStyle cssStyles[4]; + int cssBorder[4]; + QSize cssRadii[4]; // unused + for (int i = 0; i < 4; ++i) { + cssStyles[i] = QCss::BorderStyle_None; + cssBorder[i] = 0; + } + // this will parse (and cache) "border-width" as a list so the + // QCss::BorderWidth parsing below which expects a single value + // will not work as expected - which in this case does not matter + // because tableBorder is not relevant for cells. + extractor.extractBorder(cssBorder, tableCellBorderBrush, cssStyles, cssRadii); + for (int i = 0; i < 4; ++i) { + tableCellBorderStyle[i] = static_cast(cssStyles[i] - 1); + tableCellBorder[i] = static_cast(cssBorder[i]); + } + } + for (int i = 0; i < declarations.count(); ++i) { const QCss::Declaration &decl = declarations.at(i); if (decl.d->values.isEmpty()) continue; @@ -1186,6 +1212,9 @@ void QTextHtmlParserNode::applyCssDeclarations(const QVector case QCss::BorderWidth: tableBorder = extractor.lengthValue(decl); break; + case QCss::BorderCollapse: + borderCollapse = decl.borderCollapseValue(); + break; case QCss::Color: charFormat.setForeground(decl.colorValue()); break; case QCss::Float: cssFloat = QTextFrameFormat::InFlow; @@ -1654,6 +1683,11 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes) if (!c.isValid()) qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData()); node->charFormat.setBackground(c); + } else if (key == QLatin1String("bordercolor")) { + QColor c; c.setNamedColor(value); + if (!c.isValid()) + qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData()); + node->borderBrush = c; } else if (key == QLatin1String("background")) { node->applyBackgroundImage(value, resourceProvider); } else if (key == QLatin1String("cellspacing")) { diff --git a/src/gui/text/qtexthtmlparser_p.h b/src/gui/text/qtexthtmlparser_p.h index ff5f5b4c351..31f558709ff 100644 --- a/src/gui/text/qtexthtmlparser_p.h +++ b/src/gui/text/qtexthtmlparser_p.h @@ -195,8 +195,12 @@ struct QTextHtmlParserNode { int tableCellColSpan; qreal tableCellSpacing; qreal tableCellPadding; + qreal tableCellBorder[4]; + QBrush tableCellBorderBrush[4]; + QTextFrameFormat::BorderStyle tableCellBorderStyle[4]; QBrush borderBrush; QTextFrameFormat::BorderStyle borderStyle; + bool borderCollapse; int userState; int cssListIndent; @@ -290,6 +294,10 @@ public: inline int leftPadding(int i) const { return at(i).padding[MarginLeft]; } inline int rightPadding(int i) const { return at(i).padding[MarginRight]; } + inline qreal tableCellBorder(int i, int edge) const { return at(i).tableCellBorder[edge]; } + inline QTextFrameFormat::BorderStyle tableCellBorderStyle(int i, int edge) const { return at(i).tableCellBorderStyle[edge]; } + inline QBrush tableCellBorderBrush(int i, int edge) const { return at(i).tableCellBorderBrush[edge]; } + void dumpHtml(); void parse(const QString &text, const QTextDocument *resourceProvider); diff --git a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp index 58810f73c11..52e56feb5a6 100644 --- a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp +++ b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp @@ -50,6 +50,7 @@ #include #include "common.h" +// #define DEBUG_WRITE_OUTPUT QT_FORWARD_DECLARE_CLASS(QTextDocument) @@ -196,6 +197,7 @@ private: void backgroundImage_checkExpectedHtml(const QTextDocument &doc); void buildRegExpData(); static QString cssFontSizeString(const QFont &font); + void writeActualAndExpected(const char* testTag, const QString &actual, const QString &expected); QTextDocument *doc; QTextCursor cursor; @@ -224,6 +226,27 @@ QString tst_QTextDocument::cssFontSizeString(const QFont &font) : QString::number(font.pixelSize()) + QStringLiteral("px"); } +void tst_QTextDocument::writeActualAndExpected(const char *testTag, const QString &actual, const QString &expected) +{ +#ifdef DEBUG_WRITE_OUTPUT + { + QFile out(QDir::temp().absoluteFilePath(QLatin1String(testTag) + QLatin1String("-actual.html"))); + out.open(QFile::WriteOnly); + out.write(actual.toUtf8()); + out.close(); + } { + QFile out(QDir::temp().absoluteFilePath(QLatin1String(testTag) + QLatin1String("-expected.html"))); + out.open(QFile::WriteOnly); + out.write(expected.toUtf8()); + out.close(); + } +#else + Q_UNUSED(testTag) + Q_UNUSED(actual) + Q_UNUSED(expected) +#endif +} + // Testing get/set functions void tst_QTextDocument::getSetCheck() { @@ -1765,6 +1788,8 @@ void tst_QTextDocument::toHtml() QString output = doc->toHtml(); + writeActualAndExpected(QTest::currentDataTag(), output, expectedOutput); + QCOMPARE(output, expectedOutput); QDomDocument document; @@ -1962,6 +1987,8 @@ void tst_QTextDocument::toHtmlRootFrameProperties() expectedOutput.replace("DEFAULTBLOCKSTYLE", "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\""); expectedOutput.append(htmlTail); + writeActualAndExpected(QTest::currentTestFunction(), doc.toHtml(), expectedOutput); + QCOMPARE(doc.toHtml(), expectedOutput); } @@ -2734,6 +2761,8 @@ void tst_QTextDocument::backgroundImage_checkExpectedHtml(const QTextDocument &d .arg(defaultFont.weight() * 8) .arg((defaultFont.italic() ? "italic" : "normal")); + writeActualAndExpected(QTest::currentTestFunction(), doc.toHtml(), expectedHtml); + QCOMPARE(doc.toHtml(), expectedHtml); } diff --git a/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp b/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp index c5243d15355..b6917f12087 100644 --- a/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp +++ b/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp @@ -181,6 +181,11 @@ private slots: void html_tableCellBackground(); void css_bodyBackground(); void css_tableCellBackground(); + void css_tableCellBorder(); + void css_tableCellBorderShorthand(); + void css_tableCellAllBordersShorthand(); + void css_tableCellOverrideOneBorder(); + void css_tableBorderCollapse(); void css_fontWeight(); void css_float(); void css_textIndent(); @@ -1753,6 +1758,135 @@ void tst_QTextDocumentFragment::css_tableCellBackground() QCOMPARE(cell.format().background().style(), Qt::TexturePattern); } +void tst_QTextDocumentFragment::css_tableCellBorder() +{ + const char html[] = "
Foo
"; + doc->setHtml(html); + + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextBlock); + QTextTable *table = cursor.currentTable(); + QVERIFY(table); + + QTextTableCell cell = table->cellAt(0, 0); + QTextTableCellFormat cellFormat = cell.format().toTableCellFormat(); + QCOMPARE(cellFormat.leftBorder(), qreal(4)); + QCOMPARE(cellFormat.leftBorderBrush(), QBrush(QColor("red"))); + QCOMPARE(cellFormat.leftBorderStyle(), QTextFrameFormat::BorderStyle_Dashed); + + QCOMPARE(cellFormat.rightBorder(), qreal(8)); + QCOMPARE(cellFormat.rightBorderBrush(), QBrush(QColor("green"))); + QCOMPARE(cellFormat.rightBorderStyle(), QTextFrameFormat::BorderStyle_Groove); + + QCOMPARE(cellFormat.bottomBorder(), qreal(8)); + QCOMPARE(cellFormat.bottomBorderBrush(), QBrush(QColor("green"))); + QCOMPARE(cellFormat.bottomBorderStyle(), QTextFrameFormat::BorderStyle_Groove); + + QCOMPARE(cellFormat.topBorder(), qreal(8)); + QCOMPARE(cellFormat.topBorderBrush(), QBrush(QColor("green"))); + QCOMPARE(cellFormat.topBorderStyle(), QTextFrameFormat::BorderStyle_Groove); +} + +void tst_QTextDocumentFragment::css_tableCellBorderShorthand() +{ + const char html[] = "
Foo
"; + doc->setHtml(html); + + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextBlock); + QTextTable *table = cursor.currentTable(); + QVERIFY(table); + + QTextTableCell cell = table->cellAt(0, 0); + QTextTableCellFormat cellFormat = cell.format().toTableCellFormat(); + QCOMPARE(cellFormat.leftBorder(), qreal(1)); + QCOMPARE(cellFormat.leftBorderBrush(), QBrush(QColor("green"))); + QCOMPARE(cellFormat.leftBorderStyle(), QTextFrameFormat::BorderStyle_Solid); + + QCOMPARE(cellFormat.rightBorder(), qreal(2)); + QCOMPARE(cellFormat.rightBorderBrush(), QBrush(QColor("red"))); + QCOMPARE(cellFormat.rightBorderStyle(), QTextFrameFormat::BorderStyle_Dashed); + + QCOMPARE(cellFormat.bottomBorder(), qreal(3)); + QCOMPARE(cellFormat.bottomBorderBrush(), QBrush(QColor("yellow"))); + QCOMPARE(cellFormat.bottomBorderStyle(), QTextFrameFormat::BorderStyle_Dotted); + + QCOMPARE(cellFormat.topBorder(), qreal(4)); + QCOMPARE(cellFormat.topBorderBrush(), QBrush(QColor("blue"))); + QCOMPARE(cellFormat.topBorderStyle(), QTextFrameFormat::BorderStyle_DotDash); +} + +void tst_QTextDocumentFragment::css_tableCellAllBordersShorthand() +{ + const char html[] = "
Foo
"; + doc->setHtml(html); + + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextBlock); + QTextTable *table = cursor.currentTable(); + QVERIFY(table); + + QTextTableCell cell = table->cellAt(0, 0); + QTextTableCellFormat cellFormat = cell.format().toTableCellFormat(); + QCOMPARE(cellFormat.leftBorder(), qreal(2)); + QCOMPARE(cellFormat.leftBorderBrush(), QBrush(QColor("green"))); + QCOMPARE(cellFormat.leftBorderStyle(), QTextFrameFormat::BorderStyle_Dashed); + + QCOMPARE(cellFormat.rightBorder(), qreal(2)); + QCOMPARE(cellFormat.rightBorderBrush(), QBrush(QColor("green"))); + QCOMPARE(cellFormat.rightBorderStyle(), QTextFrameFormat::BorderStyle_Dashed); + + QCOMPARE(cellFormat.bottomBorder(), qreal(2)); + QCOMPARE(cellFormat.bottomBorderBrush(), QBrush(QColor("green"))); + QCOMPARE(cellFormat.bottomBorderStyle(), QTextFrameFormat::BorderStyle_Dashed); + + QCOMPARE(cellFormat.topBorder(), qreal(2)); + QCOMPARE(cellFormat.topBorderBrush(), QBrush(QColor("green"))); + QCOMPARE(cellFormat.topBorderStyle(), QTextFrameFormat::BorderStyle_Dashed); +} + +void tst_QTextDocumentFragment::css_tableCellOverrideOneBorder() +{ + const char html[] = "
Foo
"; + doc->setHtml(html); + + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextBlock); + QTextTable *table = cursor.currentTable(); + QVERIFY(table); + + QTextTableCell cell = table->cellAt(0, 0); + QTextTableCellFormat cellFormat = cell.format().toTableCellFormat(); + QCOMPARE(cellFormat.leftBorder(), qreal(4)); + QCOMPARE(cellFormat.leftBorderBrush(), QBrush(QColor("red"))); + QCOMPARE(cellFormat.leftBorderStyle(), QTextFrameFormat::BorderStyle_Solid); + + QCOMPARE(cellFormat.rightBorder(), qreal(2)); + QCOMPARE(cellFormat.rightBorderBrush(), QBrush(QColor("green"))); + QCOMPARE(cellFormat.rightBorderStyle(), QTextFrameFormat::BorderStyle_Dashed); + + QCOMPARE(cellFormat.bottomBorder(), qreal(2)); + QCOMPARE(cellFormat.bottomBorderBrush(), QBrush(QColor("green"))); + QCOMPARE(cellFormat.bottomBorderStyle(), QTextFrameFormat::BorderStyle_Dashed); + + QCOMPARE(cellFormat.topBorder(), qreal(2)); + QCOMPARE(cellFormat.topBorderBrush(), QBrush(QColor("green"))); + QCOMPARE(cellFormat.topBorderStyle(), QTextFrameFormat::BorderStyle_Dashed); +} + +void tst_QTextDocumentFragment::css_tableBorderCollapse() +{ + const char html[] = "
Foo
"; + doc->setHtml(html); + + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextBlock); + QTextTable *table = cursor.currentTable(); + QVERIFY(table); + + QCOMPARE(table->format().borderCollapse(), true); +} + void tst_QTextDocumentFragment::css_cellPaddings() { const char html[] = "" diff --git a/tests/auto/gui/text/qtexttable/tst_qtexttable.cpp b/tests/auto/gui/text/qtexttable/tst_qtexttable.cpp index f21b969aa76..7b2ff4cc100 100644 --- a/tests/auto/gui/text/qtexttable/tst_qtexttable.cpp +++ b/tests/auto/gui/text/qtexttable/tst_qtexttable.cpp @@ -50,6 +50,8 @@ typedef QList IntList; QT_FORWARD_DECLARE_CLASS(QTextDocument) +Q_DECLARE_METATYPE(QTextFrameFormat::BorderStyle); + class tst_QTextTable : public QObject { Q_OBJECT @@ -95,6 +97,8 @@ private slots: #if !defined(QT_NO_PRINTER) && defined(QT_BUILD_INTERNAL) void QTBUG31330_renderBackground(); #endif + void checkBorderAttributes_data(); + void checkBorderAttributes(); private: QTextTable *create2x2Table(); @@ -1170,5 +1174,109 @@ void tst_QTextTable::QTBUG31330_renderBackground() } #endif +void tst_QTextTable::checkBorderAttributes_data() +{ + QTest::addColumn("html"); + QTest::addColumn("topBorderWidth"); + QTest::addColumn("bottomBorderWidth"); + QTest::addColumn("leftBorderWidth"); + QTest::addColumn("rightBorderWidth"); + QTest::addColumn("topBorderStyle"); + QTest::addColumn("bottomBorderStyle"); + QTest::addColumn("leftBorderStyle"); + QTest::addColumn("rightBorderStyle"); + QTest::addColumn("topBorderBrush"); + QTest::addColumn("bottomBorderBrush"); + QTest::addColumn("leftBorderBrush"); + QTest::addColumn("rightBorderBrush"); + + const QString tableHtmlStart = QStringLiteral("" + "
Foo
" + "
OneTwo
ThreeFour
"); + QTest::newRow("1px-solid-colors") + << QString("%1" + "td {" + "border-top: 1px solid red;" + "border-bottom: 1px solid blue;" + "border-left: 1px solid green;" + "border-right: 1px solid yellow;" + "}" + "%2").arg(tableHtmlStart).arg(tableHtmlEnd) + << 1.0 << 1.0 << 1.0 << 1.0 + << QTextFrameFormat::BorderStyle_Solid << QTextFrameFormat::BorderStyle_Solid + << QTextFrameFormat::BorderStyle_Solid << QTextFrameFormat::BorderStyle_Solid + << QBrush(Qt::red) << QBrush(Qt::blue) << QBrush(Qt::darkGreen) << QBrush(Qt::yellow); + QTest::newRow("MixedWidth-solid-colors") + << QString("%1" + "td {" + "border-top: 1px solid red;" + "border-bottom: 2px solid blue;" + "border-left: 3px solid green;" + "border-right: 4px solid yellow;" + "}" + "%2").arg(tableHtmlStart).arg(tableHtmlEnd) + << 1.0 << 2.0 << 3.0 << 4.0 + << QTextFrameFormat::BorderStyle_Solid << QTextFrameFormat::BorderStyle_Solid + << QTextFrameFormat::BorderStyle_Solid << QTextFrameFormat::BorderStyle_Solid + << QBrush(Qt::red) << QBrush(Qt::blue) << QBrush(Qt::darkGreen) << QBrush(Qt::yellow); + QTest::newRow("MixedWidth-MixedStyle-colors") + << QString("%1" + "td {" + "border-top: 1px solid red;" + "border-bottom: 2px dotted blue;" + "border-left: 3px dashed green;" + "border-right: 4px inset yellow;" + "}" + "%2").arg(tableHtmlStart).arg(tableHtmlEnd) + << 1.0 << 2.0 << 3.0 << 4.0 + << QTextFrameFormat::BorderStyle_Solid << QTextFrameFormat::BorderStyle_Dotted + << QTextFrameFormat::BorderStyle_Dashed << QTextFrameFormat::BorderStyle_Inset + << QBrush(Qt::red) << QBrush(Qt::blue) << QBrush(Qt::darkGreen) << QBrush(Qt::yellow); +} + +void tst_QTextTable::checkBorderAttributes() +{ + QFETCH(QString, html); + QFETCH(qreal, topBorderWidth); + QFETCH(qreal, bottomBorderWidth); + QFETCH(qreal, leftBorderWidth); + QFETCH(qreal, rightBorderWidth); + QFETCH(QTextFrameFormat::BorderStyle, topBorderStyle); + QFETCH(QTextFrameFormat::BorderStyle, bottomBorderStyle); + QFETCH(QTextFrameFormat::BorderStyle, leftBorderStyle); + QFETCH(QTextFrameFormat::BorderStyle, rightBorderStyle); + QFETCH(QBrush, topBorderBrush); + QFETCH(QBrush, bottomBorderBrush); + QFETCH(QBrush, leftBorderBrush); + QFETCH(QBrush, rightBorderBrush); + + QTextDocument doc; + doc.setHtml(html); + QTextCursor cursor(doc.firstBlock()); + cursor.movePosition(QTextCursor::Right); + + QTextTable *currentTable = cursor.currentTable(); + QVERIFY(currentTable); + for (int row = 0; row < 2; row++) { + for (int column = 0; column < 2; column++) { + QTextTableCell cell = currentTable->cellAt(row, column); + QTextCharFormat cellFormat = cell.format(); + QCOMPARE(cellFormat.doubleProperty(QTextFormat::TableCellTopBorder), topBorderWidth); + QCOMPARE(cellFormat.doubleProperty(QTextFormat::TableCellBottomBorder), bottomBorderWidth); + QCOMPARE(cellFormat.doubleProperty(QTextFormat::TableCellLeftBorder), leftBorderWidth); + QCOMPARE(cellFormat.doubleProperty(QTextFormat::TableCellRightBorder), rightBorderWidth); + QCOMPARE(cellFormat.property(QTextFormat::TableCellTopBorderStyle), topBorderStyle); + QCOMPARE(cellFormat.property(QTextFormat::TableCellBottomBorderStyle), bottomBorderStyle); + QCOMPARE(cellFormat.property(QTextFormat::TableCellLeftBorderStyle), leftBorderStyle); + QCOMPARE(cellFormat.property(QTextFormat::TableCellRightBorderStyle), rightBorderStyle); + QCOMPARE(cellFormat.brushProperty(QTextFormat::TableCellTopBorderBrush), topBorderBrush); + QCOMPARE(cellFormat.brushProperty(QTextFormat::TableCellBottomBorderBrush), bottomBorderBrush); + QCOMPARE(cellFormat.brushProperty(QTextFormat::TableCellLeftBorderBrush), leftBorderBrush); + QCOMPARE(cellFormat.brushProperty(QTextFormat::TableCellRightBorderBrush), rightBorderBrush); + } + } +} + QTEST_MAIN(tst_QTextTable) #include "tst_qtexttable.moc"