diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp index 2f7ccfb07e..e6f58eeb14 100644 --- a/src/gui/text/qcssparser.cpp +++ b/src/gui/text/qcssparser.cpp @@ -127,6 +127,7 @@ static const QCssKnownValue properties[NumProperties - 1] = { { "image", QtImage }, { "image-position", QtImageAlignment }, { "left", Left }, + { "letter-spacing", LetterSpacing }, { "line-height", LineHeight }, { "list-style", ListStyle }, { "list-style-type", ListStyleType }, @@ -171,7 +172,8 @@ static const QCssKnownValue properties[NumProperties - 1] = { { "top", Top }, { "vertical-align", VerticalAlignment }, { "white-space", Whitespace }, - { "width", Width } + { "width", Width }, + { "word-spacing", WordSpacing } }; static const QCssKnownValue values[NumKnownValues - 1] = { @@ -386,6 +388,8 @@ static inline bool isInheritable(Property propertyId) case FontVariant: case TextTransform: case LineHeight: + case LetterSpacing: + case WordSpacing: return true; default: break; @@ -1247,6 +1251,37 @@ static void setTextDecorationFromValues(const QVector &values, QFon } } +static void setLetterSpacingFromValue(const QCss::Value &value, QFont *font) +{ + QString s = value.variant.toString(); + qreal val; + bool ok = false; + if (s.endsWith(QLatin1String("em"), Qt::CaseInsensitive)) { + s.chop(2); + val = s.toDouble(&ok); + if (ok) + font->setLetterSpacing(QFont::PercentageSpacing, (val + 1.0) * 100); + } else if (s.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) { + s.chop(2); + val = s.toDouble(&ok); + if (ok) + font->setLetterSpacing(QFont::AbsoluteSpacing, val); + } +} + +static void setWordSpacingFromValue(const QCss::Value &value, QFont *font) +{ + QString s = value.variant.toString(); + if (s.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) { + s.chop(2); + qreal val; + bool ok = false; + val = s.toDouble(&ok); + if (ok) + font->setWordSpacing(val); + } +} + static void parseShorthandFontProperty(const QVector &values, QFont *font, int *fontSizeAdjustment) { font->setStyle(QFont::StyleNormal); @@ -1319,6 +1354,8 @@ bool ValueExtractor::extractFont(QFont *font, int *fontSizeAdjustment) case Font: parseShorthandFontProperty(decl.d->values, font, fontSizeAdjustment); break; case FontVariant: setFontVariantFromValue(val, font); break; case TextTransform: setTextTransformFromValue(val, font); break; + case LetterSpacing: setLetterSpacingFromValue(val, font); break; + case WordSpacing: setWordSpacingFromValue(val, font); break; default: continue; } hit = true; diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h index f340a23cde..1f71a1c4af 100644 --- a/src/gui/text/qcssparser_p.h +++ b/src/gui/text/qcssparser_p.h @@ -199,6 +199,8 @@ enum Property { FontKerning, QtForegroundTextureCacheKey, QtIcon, + LetterSpacing, + WordSpacing, NumProperties }; diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index 04edf89430..0890614be9 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -2228,6 +2228,24 @@ QString QTextHtmlExporter::toHtml(ExportMode mode) html += (defaultCharFormat.fontItalic() ? QLatin1String("italic") : QLatin1String("normal")); html += QLatin1Char(';'); + const bool percentSpacing = (defaultCharFormat.fontLetterSpacingType() == QFont::PercentageSpacing); + if (defaultCharFormat.hasProperty(QTextFormat::FontLetterSpacing) && + (!percentSpacing || defaultCharFormat.fontLetterSpacing() != 0.0)) { + html += QLatin1String(" letter-spacing:"); + qreal value = defaultCharFormat.fontLetterSpacing(); + if (percentSpacing) // Map to em (100% == 0em) + value = (value / 100) - 1; + html += QString::number(value); + html += percentSpacing ? QLatin1String("em;") : QLatin1String("px;"); + } + + if (defaultCharFormat.hasProperty(QTextFormat::FontWordSpacing) && + defaultCharFormat.fontWordSpacing() != 0.0) { + html += QLatin1String(" word-spacing:"); + html += QString::number(defaultCharFormat.fontWordSpacing()); + html += QLatin1String("px;"); + } + // do not set text-decoration on the default font since those values are /always/ propagated // and cannot be turned off with CSS diff --git a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp index 17ce507cdf..5d7f41efda 100644 --- a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp +++ b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp @@ -102,6 +102,7 @@ private slots: void toHtmlBodyBgColorTransparent(); void toHtmlRootFrameProperties(); void toHtmlLineHeightProperties(); + void toHtmlDefaultFontSpacingProperties(); void capitalizationHtmlInExport(); void wordspacingHtmlExport(); @@ -1964,6 +1965,36 @@ void tst_QTextDocument::toHtmlLineHeightProperties() QCOMPARE(doc.toHtml(), expectedOutput); } +void tst_QTextDocument::toHtmlDefaultFontSpacingProperties() +{ + CREATE_DOC_AND_CURSOR(); + + cursor.insertText("Blah"); + + QFont fnt = doc.defaultFont(); + fnt.setLetterSpacing(QFont::AbsoluteSpacing, 13); + fnt.setWordSpacing(15); + doc.setDefaultFont(fnt); + + QString expectedOutput = QString("\n" + "" + "" + "\n" + "

Blah

" + ""); + expectedOutput = expectedOutput.arg(defaultFont.family()) + .arg(cssFontSizeString(defaultFont)) + .arg(defaultFont.weight() * 8) + .arg((defaultFont.italic() ? "italic" : "normal")); + + QCOMPARE(doc.toHtml(), expectedOutput); +} + void tst_QTextDocument::capitalizationHtmlInExport() { doc->setPlainText("Test"); diff --git a/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp b/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp index 67bb628215..3b4c55d406 100644 --- a/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp +++ b/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp @@ -266,6 +266,7 @@ private slots: void html_importImageWithoutAspectRatio(); void html_fromFirefox(); void html_emptyInlineInsideBlock(); + void css_fontAndWordSpacing(); private: inline void setHtml(const QString &html) @@ -4239,5 +4240,47 @@ void tst_QTextDocumentFragment::html_emptyInlineInsideBlock() QVERIFY(doc->firstBlock().blockFormat().leftMargin() > 0); } +void tst_QTextDocumentFragment::css_fontAndWordSpacing() +{ + { + const char html[] = "Foo"; + doc->setHtml(html); + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextCharacter); + QCOMPARE(cursor.charFormat().property(QTextFormat::FontLetterSpacing).toInt(), 13); + QCOMPARE(cursor.charFormat().property(QTextFormat::FontLetterSpacingType).toUInt(), + (uint)(QFont::AbsoluteSpacing)); + QCOMPARE(cursor.charFormat().property(QTextFormat::FontWordSpacing).toInt(), 15); + } + { + const char html[] = "Foo"; + doc->setHtml(html); + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextCharacter); + QCOMPARE(cursor.charFormat().property(QTextFormat::FontLetterSpacing).toInt(), 200); + QCOMPARE(cursor.charFormat().property(QTextFormat::FontLetterSpacingType).toUInt(), + (uint)(QFont::PercentageSpacing)); + QCOMPARE(cursor.charFormat().property(QTextFormat::FontWordSpacing).toInt(), 0); + } + { + const char html[] = "Foo"; + doc->setHtml(html); + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextCharacter); + QCOMPARE(cursor.charFormat().property(QTextFormat::FontLetterSpacing).toInt(), 100); + QCOMPARE(cursor.charFormat().property(QTextFormat::FontLetterSpacingType).toUInt(), + (uint)(QFont::PercentageSpacing)); + } + { + const char html[] = "Foo"; + doc->setHtml(html); + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextCharacter); + QCOMPARE(cursor.charFormat().property(QTextFormat::FontLetterSpacing).toInt(), 50); + QCOMPARE(cursor.charFormat().property(QTextFormat::FontLetterSpacingType).toUInt(), + (uint)(QFont::PercentageSpacing)); + } +} + QTEST_MAIN(tst_QTextDocumentFragment) #include "tst_qtextdocumentfragment.moc"