diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp index f9e6a7e335..3c98cb568d 100644 --- a/src/gui/text/qcssparser.cpp +++ b/src/gui/text/qcssparser.cpp @@ -364,6 +364,29 @@ static quint64 findKnownValue(const QString &name, const QCssKnownValue *start, return prop->id; } +static inline bool isInheritable(Property propertyId) +{ + switch (propertyId) { + case Font: + case FontFamily: + case FontSize: + case FontStyle: + case FontWeight: + case TextIndent: + case Whitespace: + case ListStyleType: + case ListStyle: + case TextAlignment: + case FontVariant: + case TextTransform: + case LineHeight: + return true; + default: + break; + } + return false; +} + /////////////////////////////////////////////////////////////////////////////// // Value Extractor ValueExtractor::ValueExtractor(const QVector &decls, const QPalette &pal) @@ -2317,6 +2340,7 @@ bool Parser::parseProperty(Declaration *decl) { decl->d->property = lexem(); decl->d->propertyId = static_cast(findKnownValue(decl->d->property, properties, NumProperties)); + decl->d->inheritable = isInheritable(decl->d->propertyId); skipSpace(); return true; } diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h index ebd9904762..75802ba36a 100644 --- a/src/gui/text/qcssparser_p.h +++ b/src/gui/text/qcssparser_p.h @@ -426,12 +426,13 @@ struct Q_GUI_EXPORT Declaration { struct DeclarationData : public QSharedData { - inline DeclarationData() : propertyId(UnknownProperty), important(false) {} + inline DeclarationData() : propertyId(UnknownProperty), important(false), inheritable(false) {} QString property; Property propertyId; QVector values; QVariant parsed; - bool important; + bool important:1; + bool inheritable:1; }; QExplicitlySharedDataPointer d; inline Declaration() : d(new DeclarationData()) {} diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp index edace3d13c..866e0046c0 100644 --- a/src/gui/text/qtexthtmlparser.cpp +++ b/src/gui/text/qtexthtmlparser.cpp @@ -1050,25 +1050,14 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent // set element specific attributes switch (id) { case Html_a: - charFormat.setAnchor(true); for (int i = 0; i < attributes.count(); i += 2) { const QString key = attributes.at(i); if (key.compare(QLatin1String("href"), Qt::CaseInsensitive) == 0 && !attributes.at(i + 1).isEmpty()) { hasHref = true; - charFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline); - charFormat.setForeground(QGuiApplication::palette().link()); } } - - break; - case Html_em: - case Html_i: - case Html_cite: - case Html_address: - case Html_var: - case Html_dfn: - charFormat.setFontItalic(true); + charFormat.setAnchor(true); break; case Html_big: charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(1)); @@ -1076,36 +1065,27 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent case Html_small: charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(-1)); break; - case Html_strong: - case Html_b: - charFormat.setFontWeight(QFont::Bold); - break; case Html_h1: - charFormat.setFontWeight(QFont::Bold); charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(3)); margin[QTextHtmlParser::MarginTop] = 18; margin[QTextHtmlParser::MarginBottom] = 12; break; case Html_h2: - charFormat.setFontWeight(QFont::Bold); charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(2)); margin[QTextHtmlParser::MarginTop] = 16; margin[QTextHtmlParser::MarginBottom] = 12; break; case Html_h3: - charFormat.setFontWeight(QFont::Bold); charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(1)); margin[QTextHtmlParser::MarginTop] = 14; margin[QTextHtmlParser::MarginBottom] = 12; break; case Html_h4: - charFormat.setFontWeight(QFont::Bold); charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(0)); margin[QTextHtmlParser::MarginTop] = 12; margin[QTextHtmlParser::MarginBottom] = 12; break; case Html_h5: - charFormat.setFontWeight(QFont::Bold); charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(-1)); margin[QTextHtmlParser::MarginTop] = 12; margin[QTextHtmlParser::MarginBottom] = 4; @@ -1114,11 +1094,7 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent margin[QTextHtmlParser::MarginTop] = 12; margin[QTextHtmlParser::MarginBottom] = 12; break; - case Html_center: - blockFormat.setAlignment(Qt::AlignCenter); - break; case Html_ul: - listStyle = QTextListFormat::ListDisc; // nested lists don't have margins, except for the toplevel one if (!isNestedList(parser)) { margin[QTextHtmlParser::MarginTop] = 12; @@ -1127,7 +1103,6 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent // no left margin as we use indenting instead break; case Html_ol: - listStyle = QTextListFormat::ListDecimal; // nested lists don't have margins, except for the toplevel one if (!isNestedList(parser)) { margin[QTextHtmlParser::MarginTop] = 12; @@ -1135,26 +1110,12 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent } // no left margin as we use indenting instead break; - case Html_code: - case Html_tt: - case Html_kbd: - case Html_samp: - charFormat.setFontFamily(QString::fromLatin1("Courier New,courier")); - // uses a fixed font, so set the property - charFormat.setFontFixedPitch(true); - break; case Html_br: text = QChar(QChar::LineSeparator); - wsm = QTextHtmlParserNode::WhiteSpacePre; break; - // ##### sub / sup case Html_pre: - charFormat.setFontFamily(QString::fromLatin1("Courier New,courier")); - wsm = WhiteSpacePre; margin[QTextHtmlParser::MarginTop] = 12; margin[QTextHtmlParser::MarginBottom] = 12; - //
 uses a fixed font
-            charFormat.setFontFixedPitch(true);
             break;
         case Html_blockquote:
             margin[QTextHtmlParser::MarginTop] = 12;
@@ -1169,28 +1130,6 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent
         case Html_dd:
             margin[QTextHtmlParser::MarginLeft] = 30;
             break;
-        case Html_u:
-            charFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
-            break;
-        case Html_s:
-            charFormat.setFontStrikeOut(true);
-            break;
-        case Html_nobr:
-            wsm = WhiteSpaceNoWrap;
-            break;
-        case Html_th:
-            charFormat.setFontWeight(QFont::Bold);
-            blockFormat.setAlignment(Qt::AlignCenter);
-            break;
-        case Html_td:
-            blockFormat.setAlignment(Qt::AlignLeft);
-            break;
-        case Html_sub:
-            charFormat.setVerticalAlignment(QTextCharFormat::AlignSubScript);
-            break;
-        case Html_sup:
-            charFormat.setVerticalAlignment(QTextCharFormat::AlignSuperScript);
-            break;
         default: break;
     }
 }
@@ -1345,6 +1284,14 @@ void QTextHtmlParserNode::applyCssDeclarations(const QVector
         case QCss::QtListNumberSuffix:
             textListNumberSuffix = decl.d->values.first().variant.toString();
             break;
+        case QCss::TextAlignment:
+            switch (identifier) {
+            case QCss::Value_Left: blockFormat.setAlignment(Qt::AlignLeft); break;
+            case QCss::Value_Center: blockFormat.setAlignment(Qt::AlignCenter); break;
+            case QCss::Value_Right: blockFormat.setAlignment(Qt::AlignRight); break;
+            default: break;
+            }
+            break;
         default: break;
         }
     }
@@ -1833,6 +1780,189 @@ void QTextHtmlParser::importStyleSheet(const QString &href)
     }
 }
 
+QVector standardDeclarationForNode(const QTextHtmlParserNode &node)
+{
+    QVector decls;
+    QCss::Declaration decl;
+    QCss::Value val;
+    switch (node.id) {
+    case Html_a:
+    case Html_u: {
+        bool needsUnderline = (node.id == Html_u) ? true : false;
+        if (node.id == Html_a) {
+            for (int i = 0; i < node.attributes.count(); i += 2) {
+                const QString key = node.attributes.at(i);
+                if (key.compare(QLatin1String("href"), Qt::CaseInsensitive) == 0
+                    && !node.attributes.at(i + 1).isEmpty()) {
+                    needsUnderline = true;
+                    decl.d->property = QLatin1String("color");
+                    decl.d->propertyId = QCss::Property::Color;
+                    val.type = QCss::Value::Color;
+                    val.variant = QVariant(QGuiApplication::palette().link());
+                    decl.d->values = QVector() << val;
+                    decl.d->inheritable = true;
+                    decls << decl;
+                    break;
+                }
+            }
+        }
+        if (needsUnderline) {
+            decl = QCss::Declaration();
+            decl.d->property = QLatin1String("text-decoration");
+            decl.d->propertyId = QCss::Property::TextDecoration;
+            val.type = QCss::Value::KnownIdentifier;
+            val.variant = QVariant(QCss::Value_Underline);
+            decl.d->values = QVector() << val;
+            decl.d->inheritable = true;
+            decls << decl;
+        }
+        break;
+    }
+    case Html_b:
+    case Html_strong:
+    case Html_h1:
+    case Html_h2:
+    case Html_h3:
+    case Html_h4:
+    case Html_h5:
+    case Html_th:
+        decl = QCss::Declaration();
+        decl.d->property = QLatin1String("font-weight");
+        decl.d->propertyId = QCss::Property::FontWeight;
+        val.type = QCss::Value::KnownIdentifier;
+        val.variant = QVariant(QCss::Value_Bold);
+        decl.d->values = QVector() << val;
+        decl.d->inheritable = true;
+        decls << decl;
+        if (node.id == Html_b || node.id == Html_strong)
+            break;
+        // Delibrate fall through
+    case Html_big:
+    case Html_small:
+        if (node.id != Html_th) {
+            decl = QCss::Declaration();
+            decl.d->property = QLatin1String("font-size");
+            decl.d->propertyId = QCss::Property::FontSize;
+            decl.d->inheritable = false;
+            val.type = QCss::Value::KnownIdentifier;
+            switch (node.id) {
+            case Html_h1: val.variant = QVariant(QCss::Value_XXLarge); break;
+            case Html_h2: val.variant = QVariant(QCss::Value_XLarge); break;
+            case Html_h3: case Html_big: val.variant = QVariant(QCss::Value_Large); break;
+            case Html_h4: val.variant = QVariant(QCss::Value_Medium); break;
+            case Html_h5: case Html_small: val.variant = QVariant(QCss::Value_Small); break;
+            default: break;
+            }
+            decl.d->values = QVector() << val;
+            decls << decl;
+            break;
+        }
+        // Delibrate fall through
+    case Html_center:
+    case Html_td:
+        decl = QCss::Declaration();
+        decl.d->property = QLatin1String("text-align");
+        decl.d->propertyId = QCss::Property::TextAlignment;
+        val.type = QCss::Value::KnownIdentifier;
+        val.variant = (node.id == Html_td) ? QVariant(QCss::Value_Left) : QVariant(QCss::Value_Center);
+        decl.d->values = QVector() << val;
+        decl.d->inheritable = true;
+        decls << decl;
+        break;
+    case Html_s:
+        decl = QCss::Declaration();
+        decl.d->property = QLatin1String("text-decoration");
+        decl.d->propertyId = QCss::Property::TextDecoration;
+        val.type = QCss::Value::KnownIdentifier;
+        val.variant = QVariant(QCss::Value_LineThrough);
+        decl.d->values = QVector() << val;
+        decl.d->inheritable = true;
+        decls << decl;
+        break;
+    case Html_em:
+    case Html_i:
+    case Html_cite:
+    case Html_address:
+    case Html_var:
+    case Html_dfn:
+        decl = QCss::Declaration();
+        decl.d->property = QLatin1String("font-style");
+        decl.d->propertyId = QCss::Property::FontStyle;
+        val.type = QCss::Value::KnownIdentifier;
+        val.variant = QVariant(QCss::Value_Italic);
+        decl.d->values = QVector() << val;
+        decl.d->inheritable = true;
+        decls << decl;
+        break;
+    case Html_sub:
+    case Html_sup:
+        decl = QCss::Declaration();
+        decl.d->property = QLatin1String("vertical-align");
+        decl.d->propertyId = QCss::Property::VerticalAlignment;
+        val.type = QCss::Value::KnownIdentifier;
+        val.variant = (node.id == Html_sub) ? QVariant(QCss::Value_Sub) : QVariant(QCss::Value_Super);
+        decl.d->values = QVector() << val;
+        decl.d->inheritable = true;
+        decls << decl;
+        break;
+    case Html_ul:
+    case Html_ol:
+        decl = QCss::Declaration();
+        decl.d->property = QLatin1String("list-style");
+        decl.d->propertyId = QCss::Property::ListStyle;
+        val.type = QCss::Value::KnownIdentifier;
+        val.variant = (node.id == Html_ul) ? QVariant(QCss::Value_Disc) : QVariant(QCss::Value_Decimal);
+        decl.d->values = QVector() << val;
+        decl.d->inheritable = true;
+        decls << decl;
+        break;
+    case Html_code:
+    case Html_tt:
+    case Html_kbd:
+    case Html_samp:
+    case Html_pre: {
+        decl = QCss::Declaration();
+        decl.d->property = QLatin1String("font-family");
+        decl.d->propertyId = QCss::Property::FontFamily;
+        QVector values;
+        val.type = QCss::Value::String;
+        val.variant = QLatin1String("Courier New");
+        values << val;
+        val.type = QCss::Value::TermOperatorComma;
+        val.variant = QVariant();
+        values << val;
+        val.type = QCss::Value::String;
+        val.variant = QLatin1String("courier");
+        values << val;
+        decl.d->values = values;
+        decl.d->inheritable = true;
+        decls << decl;
+        }
+        if (node.id != Html_pre)
+            break;
+        // Delibrate fall through
+    case Html_br:
+    case Html_nobr:
+        decl = QCss::Declaration();
+        decl.d->property = QLatin1String("whitespace");
+        decl.d->propertyId = QCss::Property::Whitespace;
+        val.type = QCss::Value::KnownIdentifier;
+        switch (node.id) {
+        case Html_br: val.variant = QVariant(QCss::Value_PreWrap); break;
+        case Html_nobr: val.variant = QVariant(QCss::Value_NoWrap); break;
+        case Html_pre: val.variant = QVariant(QCss::Value_Pre); break;
+        default: break;
+        }
+        decl.d->values = QVector() << val;
+        decl.d->inheritable = true;
+        decls << decl;
+        break;
+    default:
+        break;
+    }
+    return decls;
+}
+
 QVector QTextHtmlParser::declarationsForNode(int node) const
 {
     QVector decls;
@@ -1860,8 +1990,20 @@ QVector QTextHtmlParser::declarationsForNode(int node) const
     const char *extraPseudo = 0;
     if (nodes.at(node).id == Html_a && nodes.at(node).hasHref)
         extraPseudo = "link";
-    decls = selector.declarationsForNode(n, extraPseudo);
-
+    // Ensure that our own style is taken into consideration
+    decls = standardDeclarationForNode(nodes.at(node));
+    decls += selector.declarationsForNode(n, extraPseudo);
+    n = selector.parentNode(n);
+    while (!selector.isNullNode(n)) {
+        QVector inheritedDecls;
+        inheritedDecls = selector.declarationsForNode(n, extraPseudo);
+        for (int i = 0; i < inheritedDecls.size(); ++i) {
+            const QCss::Declaration &decl = inheritedDecls.at(i);
+            if (decl.d->inheritable)
+                decls.prepend(decl);
+        }
+        n = selector.parentNode(n);
+    }
     return decls;
 }
 
diff --git a/tests/auto/gui/text/qtextdocument/test.css b/tests/auto/gui/text/qtextdocument/test.css
new file mode 100644
index 0000000000..16a2b90666
--- /dev/null
+++ b/tests/auto/gui/text/qtextdocument/test.css
@@ -0,0 +1,3 @@
+body {
+    font: normal 400 14px/1.2 Arial;
+}
diff --git a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp
index c51d33487e..5e6b606d83 100644
--- a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp
+++ b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp
@@ -187,7 +187,7 @@ private slots:
     void QTBUG28998_linkColor();
 
     void textCursorUsageWithinContentsChange();
-
+    void cssInheritance();
 private:
     void backgroundImage_checkExpectedHtml(const QTextDocument &doc);
     void buildRegExpData();
@@ -3006,6 +3006,37 @@ void tst_QTextDocument::QTBUG27354_spaceAndSoftSpace()
         QPainter p(&image);
         document.drawContents(&p, image.rect());
     }
+    {
+        // If no p tag is specified it should not be inheriting it
+        QTextDocument td;
+        td.setHtml("Foo
  • First
"); + QTextBlock block = td.begin(); + while (block.isValid()) { + QTextBlockFormat fmt = block.blockFormat(); + QVERIFY(fmt.lineHeightType() == QTextBlockFormat::SingleHeight); + QVERIFY(fmt.lineHeight() == 0); + block = block.next(); + } + } + { + QTextDocument td; + td.setHtml("

Foo

  • First
"); + QList originalMargins; + QTextBlock block = td.begin(); + while (block.isValid()) { + originalMargins << block.blockFormat().topMargin(); + block = block.next(); + } + originalMargins[0] = 85; + td.setHtml("

Foo

  • First
"); + block = td.begin(); + int count = 0; + while (block.isValid()) { + QTextBlockFormat fmt = block.blockFormat(); + QCOMPARE(fmt.topMargin(), originalMargins.at(count++)); + block = block.next(); + } + } } class BaseDocument : public QTextDocument @@ -3124,5 +3155,110 @@ void tst_QTextDocument::textCursorUsageWithinContentsChange() QCOMPARE(handler.verticalMovementX, -1); } +void tst_QTextDocument::cssInheritance() +{ + { + QTextDocument td; + td.setHtml("" + "

Foo

Bar

Baz

"); + QTextBlock block = td.begin(); + while (block.isValid()) { + QTextBlockFormat fmt = block.blockFormat(); + QVERIFY(fmt.lineHeightType() == QTextBlockFormat::ProportionalHeight); + QVERIFY(fmt.lineHeight() == 200); + block = block.next(); + } + } + { + QTextDocument td; + td.setHtml("" + "

Foo

Bar

Baz

"); + QTextBlock block = td.begin(); + QTextBlockFormat fmt = block.blockFormat(); + QVERIFY(fmt.lineHeightType() == QTextBlockFormat::FixedHeight); + QVERIFY(fmt.lineHeight() == 40); + block = block.next(); + fmt = block.blockFormat(); + QVERIFY(fmt.lineHeightType() == QTextBlockFormat::ProportionalHeight); + QVERIFY(fmt.lineHeight() == 300); + } + { + QTextDocument td; + td.setHtml("" + "

Foo

Bar

Baz

"); + QTextBlock block = td.begin(); + while (block.isValid()) { + QVERIFY(block.blockFormat().background() == QBrush()); + QVERIFY(block.charFormat().font().bold()); + block = block.next(); + } + } + { + QTextDocument td; + td.setHtml("" + "
Foo
Bar
"); + QTextBlock block = td.begin(); + // First is the table + QTextCharFormat fmt = block.charFormat(); + QVERIFY(!fmt.font().bold()); + QVERIFY(fmt.font().italic()); + // Then the th + block = block.next(); + fmt = block.charFormat(); + QVERIFY(fmt.font().bold()); + QVERIFY(fmt.font().italic()); + // Then the td + block = block.next(); + fmt = block.charFormat(); + QVERIFY(!fmt.font().bold()); + QVERIFY(fmt.font().italic()); + } + { + QTextDocument td; + td.setHtml("" + "

This should be bold

"); + QTextBlock block = td.begin(); + // First is the p + QTextCharFormat fmt = block.charFormat(); + QVERIFY(!fmt.font().bold()); + QTextBlock::iterator it = block.begin(); + // The non bold text is first + QTextFragment currentFragment = it.fragment(); + QVERIFY(currentFragment.isValid()); + fmt = currentFragment.charFormat(); + QVERIFY(!fmt.font().bold()); + ++it; + QVERIFY(!it.atEnd()); + // Now check the "bold" text + currentFragment = it.fragment(); + QVERIFY(currentFragment.isValid()); + fmt = currentFragment.charFormat(); + QVERIFY(!fmt.font().bold()); + QVERIFY(fmt.font().italic()); + } + { + QTextDocument td; + td.setHtml("" + "

This should be bold

"); + QTextBlock block = td.begin(); + // First is the p + QTextCharFormat fmt = block.charFormat(); + QVERIFY(!fmt.font().bold()); + QTextBlock::iterator it = block.begin(); + // The non bold text is first + QTextFragment currentFragment = it.fragment(); + QVERIFY(currentFragment.isValid()); + fmt = currentFragment.charFormat(); + QVERIFY(!fmt.font().bold()); + ++it; + QVERIFY(!it.atEnd()); + // Now check the bold text + currentFragment = it.fragment(); + QVERIFY(currentFragment.isValid()); + fmt = currentFragment.charFormat(); + QVERIFY(fmt.font().bold()); + } +} + QTEST_MAIN(tst_QTextDocument) #include "tst_qtextdocument.moc" diff --git a/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp b/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp index 0fb66c8cdc..8f0d306cba 100644 --- a/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp +++ b/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp @@ -841,13 +841,13 @@ void tst_QTextDocumentFragment::unorderedListEnumeration() setHtml(QString::fromLatin1(html)); cursor.movePosition(QTextCursor::End); QVERIFY(cursor.currentList()); - QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListCircle); + QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListDisc); - const char html2[] = "
      • Blah
    "; + const char html2[] = "
        • Blah
      "; setHtml(QString::fromLatin1(html2)); cursor.movePosition(QTextCursor::End); QVERIFY(cursor.currentList()); - QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListDisc); + QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListCircle); }