Support SVG specific stroke styling properties
SVG has special properties for stroke styling. Those are usually supported by different browsers, because SVG documents can be used inside HTML files. This kind of styling is already supported by QPen and all need to be done is some plumbing to save and retrieve those values in QTextDocument when it is stored as HTML. Change-Id: I291efab5483ac5e852d117e762e203257c64b47f Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>bb10
parent
90c2cde691
commit
20521c3163
|
|
@ -36,6 +36,7 @@ struct QCssKnownValue
|
|||
quint64 id;
|
||||
};
|
||||
|
||||
// This array is sorted alphabetically.
|
||||
static const QCssKnownValue properties[NumProperties - 1] = {
|
||||
{ "-qt-background-role", QtBackgroundRole },
|
||||
{ "-qt-block-indent", QtBlockIndent },
|
||||
|
|
@ -47,6 +48,11 @@ static const QCssKnownValue properties[NumProperties - 1] = {
|
|||
{ "-qt-list-number-suffix", QtListNumberSuffix },
|
||||
{ "-qt-paragraph-type", QtParagraphType },
|
||||
{ "-qt-stroke-color", QtStrokeColor },
|
||||
{ "-qt-stroke-dasharray", QtStrokeDashArray },
|
||||
{ "-qt-stroke-dashoffset", QtStrokeDashOffset },
|
||||
{ "-qt-stroke-linecap", QtStrokeLineCap },
|
||||
{ "-qt-stroke-linejoin", QtStrokeLineJoin },
|
||||
{ "-qt-stroke-miterlimit", QtStrokeMiterLimit },
|
||||
{ "-qt-stroke-width", QtStrokeWidth },
|
||||
{ "-qt-style-features", QtStyleFeatures },
|
||||
{ "-qt-table-type", QtTableType },
|
||||
|
|
@ -160,6 +166,7 @@ static const QCssKnownValue values[NumKnownValues - 1] = {
|
|||
{ "always", Value_Always },
|
||||
{ "auto", Value_Auto },
|
||||
{ "base", Value_Base },
|
||||
{ "beveljoin", Value_BevelJoin},
|
||||
{ "bold", Value_Bold },
|
||||
{ "bottom", Value_Bottom },
|
||||
{ "bright-text", Value_BrightText },
|
||||
|
|
@ -176,6 +183,7 @@ static const QCssKnownValue values[NumKnownValues - 1] = {
|
|||
{ "dot-dot-dash", Value_DotDotDash },
|
||||
{ "dotted", Value_Dotted },
|
||||
{ "double", Value_Double },
|
||||
{ "flatcap", Value_FlatCap},
|
||||
{ "groove", Value_Groove },
|
||||
{ "highlight", Value_Highlight },
|
||||
{ "highlighted-text", Value_HighlightedText },
|
||||
|
|
@ -194,6 +202,7 @@ static const QCssKnownValue values[NumKnownValues - 1] = {
|
|||
{ "mid", Value_Mid },
|
||||
{ "middle", Value_Middle },
|
||||
{ "midlight", Value_Midlight },
|
||||
{ "miterjoin", Value_MiterJoin},
|
||||
{ "native", Value_Native },
|
||||
{ "none", Value_None },
|
||||
{ "normal", Value_Normal },
|
||||
|
|
@ -208,14 +217,18 @@ static const QCssKnownValue values[NumKnownValues - 1] = {
|
|||
{ "pre-wrap", Value_PreWrap },
|
||||
{ "ridge", Value_Ridge },
|
||||
{ "right", Value_Right },
|
||||
{ "roundcap", Value_RoundCap},
|
||||
{ "roundjoin", Value_RoundJoin},
|
||||
{ "selected", Value_Selected },
|
||||
{ "shadow", Value_Shadow },
|
||||
{ "small" , Value_Small },
|
||||
{ "small-caps", Value_SmallCaps },
|
||||
{ "solid", Value_Solid },
|
||||
{ "square", Value_Square },
|
||||
{ "squarecap", Value_SquareCap},
|
||||
{ "sub", Value_Sub },
|
||||
{ "super", Value_Super },
|
||||
{ "svgmiterjoin", Value_SvgMiterJoin},
|
||||
{ "text", Value_Text },
|
||||
{ "top", Value_Top },
|
||||
{ "transparent", Value_Transparent },
|
||||
|
|
@ -231,10 +244,10 @@ static const QCssKnownValue values[NumKnownValues - 1] = {
|
|||
};
|
||||
|
||||
//Map id to strings as they appears in the 'values' array above
|
||||
static const short indexOfId[NumKnownValues] = { 0, 41, 48, 42, 49, 50, 55, 35, 26, 71, 72, 25, 43, 5, 64, 48,
|
||||
29, 59, 60, 27, 52, 62, 6, 10, 39, 56, 19, 13, 17, 18, 20, 21, 51, 24, 46, 68, 37, 3, 2, 40, 63, 16,
|
||||
11, 58, 14, 32, 65, 33, 66, 56, 67, 34, 70, 8, 28, 38, 12, 36, 61, 7, 9, 4, 69, 54, 22, 23, 30, 31,
|
||||
1, 15, 0, 53, 45, 44 };
|
||||
static const short indexOfId[NumKnownValues] = { 0, 44, 51, 45, 52, 53, 60, 37, 28, 78, 79, 27, 46, 6, 71, 50,
|
||||
31, 65, 66, 29, 55, 69, 7, 11, 42, 62, 20, 14, 18, 19, 21, 23, 54, 26, 49, 75, 39, 3, 2, 43, 70, 17, 12,
|
||||
63, 15, 34, 72, 35, 73, 61, 74, 36, 64, 22, 56, 41, 5, 57, 67, 77, 9, 30, 40, 13, 38, 68, 8, 10, 4, 76,
|
||||
59, 24, 25, 32, 33, 1, 16, 0, 58, 48, 47 };
|
||||
|
||||
QString Value::toString() const
|
||||
{
|
||||
|
|
@ -1835,6 +1848,35 @@ bool Declaration::borderCollapseValue() const
|
|||
return d->values.at(0).toString() == "collapse"_L1;
|
||||
}
|
||||
|
||||
QList<qreal> Declaration::dashArray() const
|
||||
{
|
||||
if (d->propertyId != Property::QtStrokeDashArray || d->values.empty())
|
||||
return QList<qreal>();
|
||||
|
||||
bool isValid = true;
|
||||
QList<qreal> dashes;
|
||||
for (int i = 0; i < d->values.size(); i++) {
|
||||
Value v = d->values[i];
|
||||
// Separators must be at odd indices and Numbers at even indices.
|
||||
bool isValidSeparator = (i & 1) && v.type == Value::TermOperatorComma;
|
||||
bool isValidNumber = !(i & 1) && v.type == Value::Number;
|
||||
if (!isValidNumber && !isValidSeparator) {
|
||||
isValid = false;
|
||||
break;
|
||||
} else if (isValidNumber) {
|
||||
bool ok;
|
||||
dashes.append(v.variant.toReal(&ok));
|
||||
if (!ok) {
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isValid &= !(dashes.size() & 1);
|
||||
return isValid ? dashes : QList<qreal>();
|
||||
}
|
||||
|
||||
QIcon Declaration::iconValue() const
|
||||
{
|
||||
if (d->parsed.isValid())
|
||||
|
|
|
|||
|
|
@ -169,6 +169,11 @@ enum Property {
|
|||
QtAccent,
|
||||
QtStrokeWidth,
|
||||
QtStrokeColor,
|
||||
QtStrokeLineCap,
|
||||
QtStrokeLineJoin,
|
||||
QtStrokeMiterLimit,
|
||||
QtStrokeDashArray,
|
||||
QtStrokeDashOffset,
|
||||
QtForeground,
|
||||
NumProperties
|
||||
};
|
||||
|
|
@ -226,6 +231,13 @@ enum KnownValue {
|
|||
Value_SmallCaps,
|
||||
Value_Uppercase,
|
||||
Value_Lowercase,
|
||||
Value_SquareCap,
|
||||
Value_FlatCap,
|
||||
Value_RoundCap,
|
||||
Value_MiterJoin,
|
||||
Value_BevelJoin,
|
||||
Value_RoundJoin,
|
||||
Value_SvgMiterJoin,
|
||||
|
||||
/* keep these in same order as QPalette::ColorRole */
|
||||
Value_FirstColorRole,
|
||||
|
|
@ -451,6 +463,8 @@ struct Q_GUI_EXPORT Declaration
|
|||
|
||||
void borderImageValue(QString *image, int *cuts, TileMode *h, TileMode *v) const;
|
||||
bool borderCollapseValue() const;
|
||||
|
||||
QList<qreal> dashArray() const;
|
||||
};
|
||||
QT_CSS_DECLARE_TYPEINFO(Declaration, Q_RELOCATABLE_TYPE)
|
||||
|
||||
|
|
|
|||
|
|
@ -2757,6 +2757,51 @@ bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
|
|||
html += " -qt-stroke-width:"_L1;
|
||||
html += QString::number(outlinePen.widthF());
|
||||
html += "px;"_L1;
|
||||
|
||||
html += " -qt-stroke-linecap:"_L1;
|
||||
if (outlinePen.capStyle() == Qt::SquareCap)
|
||||
html += "squarecap;"_L1;
|
||||
else if (outlinePen.capStyle() == Qt::FlatCap)
|
||||
html += "flatcap;"_L1;
|
||||
else if (outlinePen.capStyle() == Qt::RoundCap)
|
||||
html += "roundcap;"_L1;
|
||||
|
||||
html += " -qt-stroke-linejoin:"_L1;
|
||||
if (outlinePen.joinStyle() == Qt::MiterJoin)
|
||||
html += "miterjoin;"_L1;
|
||||
else if (outlinePen.joinStyle() == Qt::SvgMiterJoin)
|
||||
html += "svgmiterjoin;"_L1;
|
||||
else if (outlinePen.joinStyle() == Qt::BevelJoin)
|
||||
html += "beveljoin;"_L1;
|
||||
else if (outlinePen.joinStyle() == Qt::RoundJoin)
|
||||
html += "roundjoin;"_L1;
|
||||
|
||||
if (outlinePen.joinStyle() == Qt::MiterJoin ||
|
||||
outlinePen.joinStyle() == Qt::SvgMiterJoin) {
|
||||
html += " -qt-stroke-miterlimit:"_L1;
|
||||
html += QString::number(outlinePen.miterLimit());
|
||||
html += u';';
|
||||
}
|
||||
|
||||
if (outlinePen.style() == Qt::CustomDashLine && !outlinePen.dashPattern().empty()) {
|
||||
html += " -qt-stroke-dasharray:"_L1;
|
||||
QString dashArrayString;
|
||||
QList<qreal> dashes = outlinePen.dashPattern();
|
||||
|
||||
for (int i = 0; i < dashes.length() - 1; i++) {
|
||||
qreal dash = dashes[i];
|
||||
dashArrayString += QString::number(dash) + u',';
|
||||
}
|
||||
|
||||
dashArrayString += QString::number(dashes.last());
|
||||
html += dashArrayString;
|
||||
html += u';';
|
||||
|
||||
html += " -qt-stroke-dashoffset:"_L1;
|
||||
html += QString::number(outlinePen.dashOffset());
|
||||
html += u';';
|
||||
}
|
||||
|
||||
attributesEmitted = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1422,6 +1422,61 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d
|
|||
}
|
||||
break;
|
||||
}
|
||||
case QCss::QtStrokeLineCap:
|
||||
{
|
||||
QPen pen = charFormat.textOutline();
|
||||
switch (identifier) {
|
||||
case QCss::Value_SquareCap: pen.setCapStyle(Qt::SquareCap); break;
|
||||
case QCss::Value_FlatCap: pen.setCapStyle(Qt::FlatCap); break;
|
||||
case QCss::Value_RoundCap: pen.setCapStyle(Qt::RoundCap); break;
|
||||
default: break;
|
||||
}
|
||||
charFormat.setTextOutline(pen);
|
||||
break;
|
||||
}
|
||||
case QCss::QtStrokeLineJoin:
|
||||
{
|
||||
QPen pen = charFormat.textOutline();
|
||||
switch (identifier) {
|
||||
case QCss::Value_MiterJoin: pen.setJoinStyle(Qt::MiterJoin); break;
|
||||
case QCss::Value_BevelJoin: pen.setJoinStyle(Qt::BevelJoin); break;
|
||||
case QCss::Value_RoundJoin: pen.setJoinStyle(Qt::RoundJoin); break;
|
||||
case QCss::Value_SvgMiterJoin: pen.setJoinStyle(Qt::SvgMiterJoin); break;
|
||||
default: break;
|
||||
}
|
||||
charFormat.setTextOutline(pen);
|
||||
break;
|
||||
}
|
||||
case QCss::QtStrokeMiterLimit:
|
||||
{
|
||||
qreal miterLimit;
|
||||
if (decl.realValue(&miterLimit)) {
|
||||
QPen pen = charFormat.textOutline();
|
||||
pen.setMiterLimit(miterLimit);
|
||||
charFormat.setTextOutline(pen);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QCss::QtStrokeDashArray:
|
||||
{
|
||||
QList<qreal> dashes = decl.dashArray();
|
||||
if (!dashes.empty()) {
|
||||
QPen pen = charFormat.textOutline();
|
||||
pen.setDashPattern(dashes);
|
||||
charFormat.setTextOutline(pen);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QCss::QtStrokeDashOffset:
|
||||
{
|
||||
qreal dashOffset;
|
||||
if (decl.realValue(&dashOffset)) {
|
||||
QPen pen = charFormat.textOutline();
|
||||
pen.setDashOffset(dashOffset);
|
||||
charFormat.setTextOutline(pen);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QCss::QtForeground:
|
||||
{
|
||||
QBrush brush = decl.brushValue();
|
||||
|
|
|
|||
|
|
@ -56,6 +56,10 @@ private slots:
|
|||
void quotedAndUnquotedIdentifiers();
|
||||
void whitespaceValues_data();
|
||||
void whitespaceValues();
|
||||
void strokeLineCapValues_data();
|
||||
void strokeLineCapValues();
|
||||
void strokeLineJoinValues_data();
|
||||
void strokeLineJoinValues();
|
||||
};
|
||||
|
||||
void tst_QCssParser::scanner_data()
|
||||
|
|
@ -1759,6 +1763,57 @@ void tst_QCssParser::whitespaceValues()
|
|||
QCOMPARE(rule.declarations.at(0).d->values.first().toString(), value);
|
||||
}
|
||||
|
||||
void tst_QCssParser::strokeLineCapValues_data()
|
||||
{
|
||||
QTest::addColumn<QString>("value");
|
||||
|
||||
QTest::newRow("flatcap") << "flatcap";
|
||||
QTest::newRow("roundcap") << "roundcap";
|
||||
QTest::newRow("squarecap") << "squarecap";
|
||||
}
|
||||
|
||||
void tst_QCssParser::strokeLineCapValues()
|
||||
{
|
||||
QFETCH(QString, value);
|
||||
QCss::Parser parser(QString("foo { -qt-stroke-linecap: %1 }").arg(value));
|
||||
QCss::StyleSheet sheet;
|
||||
QVERIFY(parser.parse(&sheet));
|
||||
|
||||
QCss::StyleRule rule = (!sheet.styleRules.isEmpty()) ?
|
||||
sheet.styleRules.at(0) : *sheet.nameIndex.begin();
|
||||
QCOMPARE(rule.declarations.size(), 1);
|
||||
|
||||
QCOMPARE(rule.declarations.at(0).d->property, QLatin1String("-qt-stroke-linecap"));
|
||||
QCOMPARE(rule.declarations.at(0).d->values.first().type, QCss::Value::KnownIdentifier);
|
||||
QCOMPARE(rule.declarations.at(0).d->values.first().toString(), value);
|
||||
}
|
||||
|
||||
void tst_QCssParser::strokeLineJoinValues_data()
|
||||
{
|
||||
QTest::addColumn<QString>("value");
|
||||
|
||||
QTest::newRow("beveljoin") << "beveljoin";
|
||||
QTest::newRow("miterjoin") << "miterjoin";
|
||||
QTest::newRow("roundjoin") << "roundjoin";
|
||||
QTest::newRow("svgmiterjoin") << "svgmiterjoin";
|
||||
}
|
||||
|
||||
void tst_QCssParser::strokeLineJoinValues()
|
||||
{
|
||||
QFETCH(QString, value);
|
||||
QCss::Parser parser(QString("foo { -qt-stroke-linejoin: %1 }").arg(value));
|
||||
QCss::StyleSheet sheet;
|
||||
QVERIFY(parser.parse(&sheet));
|
||||
|
||||
QCss::StyleRule rule = (!sheet.styleRules.isEmpty()) ?
|
||||
sheet.styleRules.at(0) : *sheet.nameIndex.begin();
|
||||
QCOMPARE(rule.declarations.size(), 1);
|
||||
|
||||
QCOMPARE(rule.declarations.at(0).d->property, QLatin1String("-qt-stroke-linejoin"));
|
||||
QCOMPARE(rule.declarations.at(0).d->values.first().type, QCss::Value::KnownIdentifier);
|
||||
QCOMPARE(rule.declarations.at(0).d->values.first().toString(), value);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QCssParser)
|
||||
#include "tst_qcssparser.moc"
|
||||
|
||||
|
|
|
|||
|
|
@ -4053,20 +4053,107 @@ void tst_QTextDocument::restoreStrokeFromHtml()
|
|||
QTextDocument document;
|
||||
QTextCursor textCursor(&document);
|
||||
QTextCharFormat textOutline;
|
||||
textOutline.setTextOutline(QPen(Qt::red, 2.3));
|
||||
textCursor.insertText("Outlined text", textOutline);
|
||||
|
||||
// Set stroke color and width
|
||||
{
|
||||
QPen pen(Qt::red, 2.3, Qt::SolidLine);
|
||||
textOutline.setTextOutline(pen);
|
||||
textCursor.insertText("Outlined text", textOutline);
|
||||
}
|
||||
|
||||
// Set Cap and Join styles
|
||||
{
|
||||
QPen pen;
|
||||
pen.setCapStyle(Qt::FlatCap);
|
||||
pen.setJoinStyle(Qt::RoundJoin);
|
||||
textOutline.setTextOutline(pen);
|
||||
textCursor.insertBlock();
|
||||
textCursor.insertText("Cap and Join Style", textOutline);
|
||||
}
|
||||
|
||||
// Set Miter limit
|
||||
{
|
||||
QPen pen;
|
||||
pen.setJoinStyle(Qt::MiterJoin);
|
||||
pen.setMiterLimit(4);
|
||||
textOutline.setTextOutline(pen);
|
||||
textCursor.insertBlock();
|
||||
textCursor.insertText("Miter Limit", textOutline);
|
||||
}
|
||||
|
||||
// Set Dash Array and Dash Offset
|
||||
{
|
||||
QPen pen;
|
||||
QList<qreal> pattern;
|
||||
const int dash = 2;
|
||||
const int gap = 4;
|
||||
pattern << dash << gap << dash << gap << dash << gap;
|
||||
pen.setDashPattern(pattern);
|
||||
pen.setDashOffset(3);
|
||||
textOutline.setTextOutline(pen);
|
||||
textCursor.insertBlock();
|
||||
textCursor.insertText("Dash Pattern", textOutline);
|
||||
}
|
||||
|
||||
{
|
||||
QTextDocument otherDocument;
|
||||
otherDocument.setHtml(document.toHtml());
|
||||
QCOMPARE(otherDocument.blockCount(), 1);
|
||||
QTextBlock block = otherDocument.firstBlock();
|
||||
QTextFragment fragment = block.begin().fragment();
|
||||
QCOMPARE(fragment.text(), QStringLiteral("Outlined text"));
|
||||
QTextCharFormat fmt = fragment.charFormat();
|
||||
QVERIFY(fmt.hasProperty(QTextCharFormat::TextOutline));
|
||||
QPen pen = fmt.textOutline();
|
||||
QCOMPARE(pen.color(), QColor(Qt::red));
|
||||
QCOMPARE(pen.widthF(), 2.3);
|
||||
QCOMPARE(otherDocument.blockCount(), document.blockCount());
|
||||
|
||||
QTextBlock block;
|
||||
QTextFragment fragment;
|
||||
QTextCharFormat fmt;
|
||||
QPen pen;
|
||||
|
||||
{
|
||||
block = otherDocument.findBlockByNumber(0);
|
||||
fragment = block.begin().fragment();
|
||||
QCOMPARE(fragment.text(), QStringLiteral("Outlined text"));
|
||||
fmt = fragment.charFormat();
|
||||
QVERIFY(fmt.hasProperty(QTextCharFormat::TextOutline));
|
||||
pen = fmt.textOutline();
|
||||
QCOMPARE(pen.color(), QColor(Qt::red));
|
||||
QCOMPARE(pen.widthF(), 2.3);
|
||||
}
|
||||
|
||||
{
|
||||
block = otherDocument.findBlockByNumber(1);
|
||||
qDebug() << block.text();
|
||||
fragment = block.begin().fragment();
|
||||
QCOMPARE(fragment.text(), QStringLiteral("Cap and Join Style"));
|
||||
fmt = fragment.charFormat();
|
||||
QVERIFY(fmt.hasProperty(QTextCharFormat::TextOutline));
|
||||
pen = fmt.textOutline();
|
||||
QCOMPARE(pen.capStyle(), Qt::FlatCap);
|
||||
QCOMPARE(pen.joinStyle(), Qt::RoundJoin);
|
||||
}
|
||||
|
||||
{
|
||||
block = otherDocument.findBlockByNumber(2);
|
||||
fragment = block.begin().fragment();
|
||||
QCOMPARE(fragment.text(), QStringLiteral("Miter Limit"));
|
||||
fmt = fragment.charFormat();
|
||||
QVERIFY(fmt.hasProperty(QTextCharFormat::TextOutline));
|
||||
pen = fmt.textOutline();
|
||||
QCOMPARE(pen.joinStyle(), Qt::MiterJoin);
|
||||
QCOMPARE(pen.miterLimit(), 4);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
block = otherDocument.findBlockByNumber(3);
|
||||
fragment = block.begin().fragment();
|
||||
QCOMPARE(fragment.text(), QStringLiteral("Dash Pattern"));
|
||||
fmt = fragment.charFormat();
|
||||
QVERIFY(fmt.hasProperty(QTextCharFormat::TextOutline));
|
||||
pen = fmt.textOutline();
|
||||
QCOMPARE(pen.dashOffset(), 3);
|
||||
QList<qreal> pattern;
|
||||
const int dash = 2;
|
||||
const int gap = 4;
|
||||
pattern << dash << gap << dash << gap << dash << gap;
|
||||
QCOMPARE(pen.dashPattern(), pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue