Export the letter and word spacing settings set on the default format

When the default format has letter and word spacing set then these
should be exported in the HTML's body tag. This also adds support
for the reading of letter-spacing and word-spacing set too, so that
the same html outputted can be read back in.

Fixes: QTBUG-83718
Change-Id: Ic4afca21eb05efb779dbf99c6b3c13373e851f15
Pick-to: 5.15
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Andy Shaw 2020-05-11 14:34:27 +02:00
parent 60c6f4a51a
commit 96cea3b168
5 changed files with 132 additions and 1 deletions

View File

@ -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<QCss::Value> &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<QCss::Value> &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;

View File

@ -199,6 +199,8 @@ enum Property {
FontKerning,
QtForegroundTextureCacheKey,
QtIcon,
LetterSpacing,
WordSpacing,
NumProperties
};

View File

@ -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

View File

@ -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("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
"\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" />"
"<meta charset=\"utf-8\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head>"
"<body style=\" font-family:'%1'; font-size:%2; "
"font-weight:%3; font-style:%4; letter-spacing:13px; "
"word-spacing:15px;\">\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Blah</p>"
"</body></html>");
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");

View File

@ -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[] = "<body style=\"letter-spacing:13px; word-spacing:15px;\">Foo</span>";
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[] = "<body style=\"letter-spacing:1em; word-spacing:0px;\">Foo</span>";
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[] = "<body style=\"letter-spacing:0em;\">Foo</span>";
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[] = "<body style=\"letter-spacing:-0.5em;\">Foo</span>";
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"