Compress consective StyledText whitespaces into a single space.
authorAndrew den Exter <andrew.den-exter@nokia.com>
Mon, 30 Jan 2012 06:37:19 +0000 (16:37 +1000)
committerQt by Nokia <qt-info@nokia.com>
Thu, 2 Feb 2012 01:36:06 +0000 (02:36 +0100)
This improves the correctness of the parser and prevents line feed
characters appearing as unrendereable characters.

Change-Id: I54e8be011ba4d9ad65ee2142e6b1f423a7579352
Reviewed-by: Yann Bodson <yann.bodson@nokia.com>
Reviewed-by: Martin Jones <martin.jones@nokia.com>

src/quick/util/qdeclarativestyledtext.cpp
tests/auto/qtquick2/qdeclarativestyledtext/tst_qdeclarativestyledtext.cpp

index 38daf7a..d34601d 100644 (file)
@@ -81,11 +81,12 @@ public:
 
     QDeclarativeStyledTextPrivate(const QString &t, QTextLayout &l)
         : text(t), layout(l), baseFont(layout.font()), hasNewLine(false)
-        , preFormat(false)
+        , preFormat(false), prependSpace(false), hasSpace(true)
     {
     }
 
     void parse();
+    void appendText(const QString &textIn, int start, int length, QString &textOut);
     bool parseTag(const QChar *&ch, const QString &textIn, QString &textOut, QTextCharFormat &format);
     bool parseCloseTag(const QChar *&ch, const QString &textIn, QString &textOut);
     void parseEntity(const QChar *&ch, const QString &textIn, QString &textOut);
@@ -96,6 +97,7 @@ public:
     QPair<QStringRef,QStringRef> parseAttribute(const QChar *&ch, const QString &textIn);
     QStringRef parseValue(const QChar *&ch, const QString &textIn);
 
+
     inline void skipSpace(const QChar *&ch) {
         while (ch->isSpace() && !ch->isNull())
             ++ch;
@@ -110,6 +112,8 @@ public:
     QStack<List> listStack;
     bool hasNewLine;
     bool preFormat;
+    bool prependSpace;
+    bool hasSpace;
 
     static const QChar lessThan;
     static const QChar greaterThan;
@@ -121,6 +125,8 @@ public:
     static const QChar bullet;
     static const QChar disc;
     static const QChar square;
+    static const QChar lineFeed;
+    static const QChar space;
     static const int tabsize = 6;
 };
 
@@ -134,6 +140,8 @@ const QChar QDeclarativeStyledTextPrivate::ampersand(QLatin1Char('&'));
 const QChar QDeclarativeStyledTextPrivate::bullet(0x2022);
 const QChar QDeclarativeStyledTextPrivate::disc(0x25e6);
 const QChar QDeclarativeStyledTextPrivate::square(0x25a1);
+const QChar QDeclarativeStyledTextPrivate::lineFeed(QLatin1Char('\n'));
+const QChar QDeclarativeStyledTextPrivate::space(QLatin1Char(' '));
 
 QDeclarativeStyledText::QDeclarativeStyledText(const QString &string, QTextLayout &layout)
 : d(new QDeclarativeStyledTextPrivate(string, layout))
@@ -165,22 +173,18 @@ void QDeclarativeStyledTextPrivate::parse()
     int textLength = 0;
     int rangeStart = 0;
     bool formatChanged = false;
+
     const QChar *ch = text.constData();
     while (!ch->isNull()) {
         if (*ch == lessThan) {
             if (textLength) {
-                QStringRef ref = QStringRef(&text, textStart, textLength);
-                const QChar *c = ref.constData();
-                bool isWhiteSpace = true;
-                for (int i = 0; isWhiteSpace && (i < textLength); ++c, ++i) {
-                    if (!c->isSpace())
-                        isWhiteSpace = false;
-                }
-                if (!isWhiteSpace) {
-                    drawText.append(ref);
-                    hasNewLine = false;
-                }
+                appendText(text, textStart, textLength, drawText);
+            } else if (prependSpace) {
+                drawText.append(space);
+                prependSpace = false;
+                hasSpace = true;
             }
+
             if (rangeStart != drawText.length() && formatStack.count()) {
                 if (formatChanged) {
                     QTextLayout::FormatRange formatRange;
@@ -216,13 +220,25 @@ void QDeclarativeStyledTextPrivate::parse()
             textLength = 0;
         } else if (*ch == ampersand) {
             ++ch;
-            drawText.append(QStringRef(&text, textStart, textLength));
+            appendText(text, textStart, textLength, drawText);
             parseEntity(ch, text, drawText);
             textStart = ch - text.constData() + 1;
             textLength = 0;
-        } else if (preFormat && ch->isSpace()) {
-            drawText.append(QStringRef(&text, textStart, textLength));
-            drawText.append(QChar(QChar::Nbsp));
+        } else if (ch->isSpace()) {
+            if (textLength)
+                appendText(text, textStart, textLength, drawText);
+            if (!preFormat) {
+                prependSpace = !hasSpace;
+                for (const QChar *n = ch + 1; !n->isNull() && n->isSpace(); ++n)
+                    ch = n;
+                hasNewLine = false;
+            } else  if (*ch == lineFeed) {
+                drawText.append(QChar(QChar::LineSeparator));
+                hasNewLine = true;
+            } else {
+                drawText.append(QChar(QChar::Nbsp));
+                hasNewLine = false;
+            }
             textStart = ch - text.constData() + 1;
             textLength = 0;
         } else {
@@ -232,7 +248,7 @@ void QDeclarativeStyledTextPrivate::parse()
             ++ch;
     }
     if (textLength)
-        drawText.append(QStringRef(&text, textStart, textLength));
+        appendText(text, textStart, textLength, drawText);
     if (rangeStart != drawText.length() && formatStack.count()) {
         if (formatChanged) {
             QTextLayout::FormatRange formatRange;
@@ -249,6 +265,16 @@ void QDeclarativeStyledTextPrivate::parse()
     layout.setAdditionalFormats(ranges);
 }
 
+void QDeclarativeStyledTextPrivate::appendText(const QString &textIn, int start, int length, QString &textOut)
+{
+    if (prependSpace)
+        textOut.append(space);
+    textOut.append(QStringRef(&textIn, start, length));
+    prependSpace = false;
+    hasSpace = false;
+    hasNewLine = false;
+}
+
 bool QDeclarativeStyledTextPrivate::parseTag(const QChar *&ch, const QString &textIn, QString &textOut, QTextCharFormat &format)
 {
     skipSpace(ch);
@@ -267,6 +293,8 @@ bool QDeclarativeStyledTextPrivate::parseTag(const QChar *&ch, const QString &te
                     return true;
                 } else if (tagLength == 2 && tag.at(1) == QLatin1Char('r')) {
                     textOut.append(QChar(QChar::LineSeparator));
+                    hasSpace = true;
+                    prependSpace = false;
                     return false;
                 }
             } else if (char0 == QLatin1Char('i')) {
@@ -278,6 +306,8 @@ bool QDeclarativeStyledTextPrivate::parseTag(const QChar *&ch, const QString &te
                 if (tagLength == 1) {
                     if (!hasNewLine)
                         textOut.append(QChar::LineSeparator);
+                    hasSpace = true;
+                    prependSpace = false;
                 } else if (tag == QLatin1String("pre")) {
                     preFormat = true;
                     if (!hasNewLine)
@@ -303,6 +333,8 @@ bool QDeclarativeStyledTextPrivate::parseTag(const QChar *&ch, const QString &te
                     static const qreal scaling[] = { 2.0, 1.5, 1.2, 1.0, 0.8, 0.7 };
                     if (!hasNewLine)
                         textOut.append(QChar::LineSeparator);
+                    hasSpace = true;
+                    prependSpace = false;
                     format.setFontPointSize(baseFont.pointSize() * scaling[level - 1]);
                     format.setFontWeight(QFont::Bold);
                     return true;
@@ -407,12 +439,14 @@ bool QDeclarativeStyledTextPrivate::parseCloseTag(const QChar *&ch, const QStrin
                 if (tagLength == 1) {
                     textOut.append(QChar::LineSeparator);
                     hasNewLine = true;
+                    hasSpace = true;
                     return false;
                 } else if (tag == QLatin1String("pre")) {
                     preFormat = false;
                     if (!hasNewLine)
                         textOut.append(QChar::LineSeparator);
                     hasNewLine = true;
+                    hasSpace = true;
                     return true;
                 }
             } else if (char0 == QLatin1Char('u')) {
@@ -429,6 +463,7 @@ bool QDeclarativeStyledTextPrivate::parseCloseTag(const QChar *&ch, const QStrin
             } else if (char0 == QLatin1Char('h') && tagLength == 2) {
                 textOut.append(QChar::LineSeparator);
                 hasNewLine = true;
+                hasSpace = true;
                 return true;
             } else if (tag == QLatin1String("font")) {
                 return true;
index c92a99c..bfbc2cd 100644 (file)
@@ -90,6 +90,7 @@ void tst_qdeclarativestyledtext::textOutput_data()
 
     QTest::newRow("bold") << "<b>bold</b>" << "bold" << (FormatList() << Format(Format::Bold, 0, 4));
     QTest::newRow("italic") << "<i>italic</i>" << "italic" << (FormatList() << Format(Format::Italic, 0, 6));
+    QTest::newRow("underline") << "<u>underline</u>" << "underline" << (FormatList() << Format(Format::Underline, 0, 9));
     QTest::newRow("strong") << "<strong>strong</strong>" << "strong" << (FormatList() << Format(Format::Bold, 0, 6));
     QTest::newRow("missing >") << "<b>text</b" << "text" << (FormatList() << Format(Format::Bold, 0, 4));
     QTest::newRow("missing b>") << "<b>text</" << "text" << (FormatList() << Format(Format::Bold, 0, 4));
@@ -134,6 +135,17 @@ void tst_qdeclarativestyledtext::textOutput_data()
     QTest::newRow("h6") << "<h6>head" << QChar(QChar::LineSeparator) + QLatin1String("head") << (FormatList() << Format(Format::Bold, 0, 5));
     QTest::newRow("h7") << "<h7>head" << "head" << FormatList();
     QTest::newRow("pre") << "normal<pre>pre text</pre>normal" << QLatin1String("normal") + QChar(QChar::LineSeparator) + QLatin1String("pre") + QChar(QChar::Nbsp) + QLatin1String("text") + QChar(QChar::LineSeparator) + QLatin1String("normal") << (FormatList() << Format(0, 6, 9));
+    QTest::newRow("pre lb") << "normal<pre>pre\n text</pre>normal" << QLatin1String("normal") + QChar(QChar::LineSeparator) + QLatin1String("pre") + QChar(QChar::LineSeparator) + QChar(QChar::Nbsp) + QLatin1String("text") + QChar(QChar::LineSeparator) + QLatin1String("normal") << (FormatList() << Format(0, 6, 10));
+    QTest::newRow("line feed") << "line\nfeed" << "line feed" << FormatList();
+    QTest::newRow("leading whitespace") << " leading whitespace" << "leading whitespace" << FormatList();
+    QTest::newRow("trailing whitespace") << "trailing whitespace " << "trailing whitespace" << FormatList();
+    QTest::newRow("consecutive whitespace") << " consecutive  \t \n  whitespace" << "consecutive whitespace" << FormatList();
+    QTest::newRow("space after newline") << "text<br/> more text" << QLatin1String("text") + QChar(QChar::LineSeparator) + QLatin1String("more text") << FormatList();
+    QTest::newRow("space after paragraph") << "text<p> more text</p> more text" << QLatin1String("text") + QChar(QChar::LineSeparator) + QLatin1String("more text") + QChar(QChar::LineSeparator) + QLatin1String("more text") << FormatList();
+    QTest::newRow("space in header") << "<h1> head</h1> " << QChar(QChar::LineSeparator) + QLatin1String("head") + QChar(QChar::LineSeparator) << (FormatList() << Format(Format::Bold, 0, 5));
+    QTest::newRow("space before bold") << "this is <b>bold</b>" << "this is bold" << (FormatList() << Format(Format::Bold, 8, 4));
+    QTest::newRow("space leading bold") << "this is<b> bold</b>" << "this is bold" << (FormatList() << Format(Format::Bold, 7, 5));
+    QTest::newRow("space trailing bold") << "this is <b>bold </b>" << "this is bold " << (FormatList() << Format(Format::Bold, 8, 5));
 }
 
 void tst_qdeclarativestyledtext::textOutput()