diff options
Diffstat (limited to 'src/gui/text/qtexthtmlparser.cpp')
-rw-r--r-- | src/gui/text/qtexthtmlparser.cpp | 212 |
1 files changed, 117 insertions, 95 deletions
diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp index aef5e34d7b..bc2200697d 100644 --- a/src/gui/text/qtexthtmlparser.cpp +++ b/src/gui/text/qtexthtmlparser.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qtexthtmlparser_p.h" @@ -449,17 +413,17 @@ static const QTextHtmlElement elements[Html_NumElements]= { { "var", Html_var, QTextHtmlElement::DisplayInline }, }; -static bool operator<(const QString &str, const QTextHtmlElement &e) +static bool operator<(QStringView str, const QTextHtmlElement &e) { return str < QLatin1StringView(e.name); } -static bool operator<(const QTextHtmlElement &e, const QString &str) +static bool operator<(const QTextHtmlElement &e, QStringView str) { return QLatin1StringView(e.name) < str; } -static const QTextHtmlElement *lookupElementHelper(const QString &element) +static const QTextHtmlElement *lookupElementHelper(QStringView element) { const QTextHtmlElement *start = &elements[0]; const QTextHtmlElement *end = &elements[Html_NumElements]; @@ -469,7 +433,7 @@ static const QTextHtmlElement *lookupElementHelper(const QString &element) return e; } -int QTextHtmlParser::lookupElement(const QString &element) +int QTextHtmlParser::lookupElement(QStringView element) { const QTextHtmlElement *e = lookupElementHelper(element); if (!e) @@ -524,7 +488,7 @@ QTextHtmlParserNode *QTextHtmlParser::newNode(int parent) bool reuseLastNode = true; - if (nodes.count() == 1) { + if (nodes.size() == 1) { reuseLastNode = false; } else if (lastNode->tag.isEmpty()) { @@ -532,7 +496,7 @@ QTextHtmlParserNode *QTextHtmlParser::newNode(int parent) reuseLastNode = true; } else { // last node is a text node (empty tag) with some text - if (lastNode->text.length() == 1 && lastNode->text.at(0).isSpace()) { + if (lastNode->text.size() == 1 && lastNode->text.at(0).isSpace()) { int lastSibling = count() - 2; while (lastSibling @@ -579,7 +543,7 @@ void QTextHtmlParser::parse(const QString &text, const QTextDocument *_resourceP nodes.append(new QTextHtmlParserNode); txt = text; pos = 0; - len = txt.length(); + len = txt.size(); textEditMode = false; resourceProvider = _resourceProvider; parse(); @@ -708,7 +672,7 @@ void QTextHtmlParser::parseTag() resolveNode(); #ifndef QT_NO_CSSPARSER - const int nodeIndex = nodes.count() - 1; // this new node is always the last + const int nodeIndex = nodes.size() - 1; // this new node is always the last node->applyCssDeclarations(declarationsForNode(nodeIndex), resourceProvider); #endif applyAttributes(node->attributes); @@ -807,7 +771,7 @@ QString QTextHtmlParser::parseEntity(QStringView entity) if (!resolved.isNull()) return QString(resolved); - if (entity.length() > 1 && entity.at(0) == u'#') { + if (entity.size() > 1 && entity.at(0) == u'#') { entity = entity.mid(1); // removing leading # int base = 10; @@ -874,7 +838,7 @@ QString QTextHtmlParser::parseWord() while (pos < len) { QChar c = txt.at(pos++); // Allow for escaped single quotes as they may be part of the string - if (c == u'\'' && (txt.length() > 1 && txt.at(pos - 2) != u'\\')) + if (c == u'\'' && (txt.size() > 1 && txt.at(pos - 2) != u'\\')) break; else word += c; @@ -912,21 +876,21 @@ QTextHtmlParserNode *QTextHtmlParser::resolveParent() n = at(n).parent; if (!n) { - nodes.insert(nodes.count() - 1, new QTextHtmlParserNode); - nodes.insert(nodes.count() - 1, new QTextHtmlParserNode); + nodes.insert(nodes.size() - 1, new QTextHtmlParserNode); + nodes.insert(nodes.size() - 1, new QTextHtmlParserNode); - QTextHtmlParserNode *table = nodes[nodes.count() - 3]; + QTextHtmlParserNode *table = nodes[nodes.size() - 3]; table->parent = p; table->id = Html_table; table->tag = "table"_L1; - table->children.append(nodes.count() - 2); // add row as child + table->children.append(nodes.size() - 2); // add row as child - QTextHtmlParserNode *row = nodes[nodes.count() - 2]; - row->parent = nodes.count() - 3; // table as parent + QTextHtmlParserNode *row = nodes[nodes.size() - 2]; + row->parent = nodes.size() - 3; // table as parent row->id = Html_tr; row->tag = "tr"_L1; - p = nodes.count() - 2; + p = nodes.size() - 2; node = nodes.last(); // re-initialize pointer } } @@ -937,12 +901,12 @@ QTextHtmlParserNode *QTextHtmlParser::resolveParent() n = at(n).parent; if (!n) { - nodes.insert(nodes.count() - 1, new QTextHtmlParserNode); - QTextHtmlParserNode *table = nodes[nodes.count() - 2]; + nodes.insert(nodes.size() - 1, new QTextHtmlParserNode); + QTextHtmlParserNode *table = nodes[nodes.size() - 2]; table->parent = p; table->id = Html_table; table->tag = "table"_L1; - p = nodes.count() - 2; + p = nodes.size() - 2; node = nodes.last(); // re-initialize pointer } } @@ -990,7 +954,7 @@ QTextHtmlParserNode *QTextHtmlParser::resolveParent() node->parent = p; // makes it easier to traverse the tree, later - nodes[p]->children.append(nodes.count() - 1); + nodes[p]->children.append(nodes.size() - 1); return node; } @@ -1065,7 +1029,7 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent // set element specific attributes switch (id) { case Html_a: - for (int i = 0; i < attributes.count(); i += 2) { + for (int i = 0; i < attributes.size(); i += 2) { const QString key = attributes.at(i); if (key.compare("href"_L1, Qt::CaseInsensitive) == 0 && !attributes.at(i + 1).isEmpty()) { @@ -1153,7 +1117,7 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent #ifndef QT_NO_CSSPARSER void QTextHtmlParserNode::setListStyle(const QList<QCss::Value> &cssValues) { - for (int i = 0; i < cssValues.count(); ++i) { + for (int i = 0; i < cssValues.size(); ++i) { if (cssValues.at(i).type == QCss::Value::KnownIdentifier) { switch (static_cast<QCss::KnownValue>(cssValues.at(i).variant.toInt())) { case QCss::Value_None: hasOwnListStyle = true; listStyle = QTextListFormat::ListStyleUndefined; break; @@ -1217,7 +1181,7 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d QCss::ValueExtractor extractor(declarations); extractor.extractBox(margin, padding); - if (id == Html_td || id == Html_th) { + auto getBorderValues = [&extractor](qreal *borderWidth, QBrush *borderBrush, QTextFrameFormat::BorderStyle *borderStyles) { QCss::BorderStyle cssStyles[4]; int cssBorder[4]; QSize cssRadii[4]; // unused @@ -1229,20 +1193,24 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d // QCss::BorderWidth parsing below which expects a single value // will not work as expected - which in this case does not matter // because tableBorder is not relevant for cells. - extractor.extractBorder(cssBorder, tableCellBorderBrush, cssStyles, cssRadii); + bool hit = extractor.extractBorder(cssBorder, borderBrush, cssStyles, cssRadii); for (int i = 0; i < 4; ++i) { - tableCellBorderStyle[i] = toQTextFrameFormat(cssStyles[i]); - tableCellBorder[i] = static_cast<qreal>(cssBorder[i]); + borderStyles[i] = toQTextFrameFormat(cssStyles[i]); + borderWidth[i] = static_cast<qreal>(cssBorder[i]); } - } + return hit; + }; + + if (id == Html_td || id == Html_th) + getBorderValues(tableCellBorder, tableCellBorderBrush, tableCellBorderStyle); - for (int i = 0; i < declarations.count(); ++i) { + for (int i = 0; i < declarations.size(); ++i) { const QCss::Declaration &decl = declarations.at(i); if (decl.d->values.isEmpty()) continue; QCss::KnownValue identifier = QCss::UnknownValue; - if (decl.d->values.first().type == QCss::Value::KnownIdentifier) - identifier = static_cast<QCss::KnownValue>(decl.d->values.first().variant.toInt()); + if (decl.d->values.constFirst().type == QCss::Value::KnownIdentifier) + identifier = static_cast<QCss::KnownValue>(decl.d->values.constFirst().variant.toInt()); switch (decl.d->propertyId) { case QCss::BorderColor: borderBrush = QBrush(decl.colorValue()); break; @@ -1256,6 +1224,19 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d tableBorder = borders[0]; } break; + case QCss::Border: { + qreal tblBorder[4]; + QBrush tblBorderBrush[4]; + QTextFrameFormat::BorderStyle tblBorderStyle[4]; + if (getBorderValues(tblBorder, tblBorderBrush, tblBorderStyle)) { + tableBorder = tblBorder[0]; + if (tblBorderBrush[0].color().isValid()) + borderBrush = tblBorderBrush[0]; + if (tblBorderStyle[0] != static_cast<QTextFrameFormat::BorderStyle>(-1)) + borderStyle = tblBorderStyle[0]; + } + } + break; case QCss::BorderCollapse: borderCollapse = decl.borderCollapseValue(); break; @@ -1269,10 +1250,10 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d } break; case QCss::QtBlockIndent: - blockFormat.setIndent(decl.d->values.first().variant.toInt()); + blockFormat.setIndent(decl.d->values.constFirst().variant.toInt()); break; case QCss::QtLineHeightType: { - QString lineHeightTypeName = decl.d->values.first().variant.toString(); + QString lineHeightTypeName = decl.d->values.constFirst().variant.toString(); QTextBlockFormat::LineHeightTypes lineHeightType; if (lineHeightTypeName.compare("proportional"_L1, Qt::CaseInsensitive) == 0) lineHeightType = QTextBlockFormat::ProportionalHeight; @@ -1301,7 +1282,7 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d lineHeightType = QTextBlockFormat::MinimumHeight; } else { bool ok; - QCss::Value cssValue = decl.d->values.first(); + QCss::Value cssValue = decl.d->values.constFirst(); QString value = cssValue.toString(); lineHeight = value.toDouble(&ok); if (ok) { @@ -1333,19 +1314,19 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d hasCssListIndent = true; break; case QCss::QtParagraphType: - if (decl.d->values.first().variant.toString().compare("empty"_L1, Qt::CaseInsensitive) == 0) + if (decl.d->values.constFirst().variant.toString().compare("empty"_L1, Qt::CaseInsensitive) == 0) isEmptyParagraph = true; break; case QCss::QtTableType: - if (decl.d->values.first().variant.toString().compare("frame"_L1, Qt::CaseInsensitive) == 0) + if (decl.d->values.constFirst().variant.toString().compare("frame"_L1, Qt::CaseInsensitive) == 0) isTextFrame = true; - else if (decl.d->values.first().variant.toString().compare("root"_L1, Qt::CaseInsensitive) == 0) { + else if (decl.d->values.constFirst().variant.toString().compare("root"_L1, Qt::CaseInsensitive) == 0) { isTextFrame = true; isRootFrame = true; } break; case QCss::QtUserState: - userState = decl.d->values.first().variant.toInt(); + userState = decl.d->values.constFirst().variant.toInt(); break; case QCss::Whitespace: switch (identifier) { @@ -1399,10 +1380,10 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d setListStyle(decl.d->values); break; case QCss::QtListNumberPrefix: - textListNumberPrefix = decl.d->values.first().variant.toString(); + textListNumberPrefix = decl.d->values.constFirst().variant.toString(); break; case QCss::QtListNumberSuffix: - textListNumberSuffix = decl.d->values.first().variant.toString(); + textListNumberSuffix = decl.d->values.constFirst().variant.toString(); break; case QCss::TextAlignment: switch (identifier) { @@ -1417,12 +1398,43 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d { if (resourceProvider != nullptr && QTextDocumentPrivate::get(resourceProvider) != nullptr) { bool ok; - qint64 searchKey = decl.d->values.first().variant.toLongLong(&ok); + qint64 searchKey = decl.d->values.constFirst().variant.toLongLong(&ok); if (ok) applyForegroundImage(searchKey, resourceProvider); } break; } + case QCss::QtStrokeColor: + { + QPen pen = charFormat.textOutline(); + pen.setStyle(Qt::SolidLine); + pen.setColor(decl.colorValue()); + charFormat.setTextOutline(pen); + break; + } + case QCss::QtStrokeWidth: + { + qreal width; + if (decl.realValue(&width, "px")) { + QPen pen = charFormat.textOutline(); + pen.setWidthF(width); + charFormat.setTextOutline(pen); + } + break; + } + case QCss::QtForeground: + { + QBrush brush = decl.brushValue(); + charFormat.setForeground(brush); + break; + } + case QCss::MaximumWidth: + if (id == Html_img) { + auto imageFormat = charFormat.toImageFormat(); + imageFormat.setMaximumWidth(extractor.textLength(decl)); + charFormat = imageFormat; + } + break; default: break; } } @@ -1523,7 +1535,7 @@ void QTextHtmlParserNode::applyBackgroundImage(const QString &url, const QTextDo bool QTextHtmlParserNode::hasOnlyWhitespace() const { - for (int i = 0; i < text.length(); ++i) + for (int i = 0; i < text.size(); ++i) if (!text.at(i).isSpace() || text.at(i) == QChar::LineSeparator) return false; return true; @@ -1573,7 +1585,7 @@ void QTextHtmlParserNode::parseStyleAttribute(const QString &value, const QTextD QCss::Parser parser(css); QCss::StyleSheet sheet; parser.parse(&sheet, Qt::CaseInsensitive); - if (sheet.styleRules.count() != 1) return; + if (sheet.styleRules.size() != 1) return; applyCssDeclarations(sheet.styleRules.at(0).declarations, resourceProvider); } #endif @@ -1611,12 +1623,12 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes) QString linkHref; QString linkType; - if (attributes.count() % 2 == 1) + if (attributes.size() % 2 == 1) return; QTextHtmlParserNode *node = nodes.last(); - for (int i = 0; i < attributes.count(); i += 2) { + for (int i = 0; i < attributes.size(); i += 2) { QString key = attributes.at(i); QString value = attributes.at(i + 1); @@ -1630,10 +1642,9 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes) node->charFormat.setProperty(QTextFormat::FontSizeAdjustment, n); } else if (key == "face"_L1) { if (value.contains(u',')) { - const QStringList values = value.split(u','); QStringList families; - for (const QString &family : values) - families << family.trimmed(); + for (auto family : value.tokenize(u',')) + families << family.trimmed().toString(); node->charFormat.setFontFamilies(families); } else { node->charFormat.setFontFamilies(QStringList(value)); @@ -1670,6 +1681,16 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes) else if (value == "none"_L1) node->listStyle = QTextListFormat::ListStyleUndefined; } + } else if (key == "start"_L1) { + setIntAttribute(&node->listStart, value); + } + break; + case Html_li: + if (key == "class"_L1) { + if (value == "unchecked"_L1) + node->blockFormat.setMarker(QTextBlockFormat::MarkerType::Unchecked); + else if (value == "checked"_L1) + node->blockFormat.setMarker(QTextBlockFormat::MarkerType::Checked); } break; case Html_a: @@ -1724,7 +1745,8 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes) } break; case Html_table: - if (key == "border"_L1) { + // If table border already set through css style, prefer that one otherwise consider this value + if (key == "border"_L1 && !node->tableBorder) { setFloatAttribute(&node->tableBorder, value); } else if (key == "bgcolor"_L1) { QColor c = QColor::fromString(value); @@ -1928,7 +1950,7 @@ void QTextHtmlStyleSelector::freeNode(NodePtr) const void QTextHtmlParser::resolveStyleSheetImports(const QCss::StyleSheet &sheet) { - for (int i = 0; i < sheet.importRules.count(); ++i) { + for (int i = 0; i < sheet.importRules.size(); ++i) { const QCss::ImportRule &rule = sheet.importRules.at(i); if (rule.media.isEmpty() || rule.media.contains("screen"_L1, Qt::CaseInsensitive)) importStyleSheet(rule.href); @@ -1939,7 +1961,7 @@ void QTextHtmlParser::importStyleSheet(const QString &href) { if (!resourceProvider) return; - for (int i = 0; i < externalStyleSheets.count(); ++i) + for (int i = 0; i < externalStyleSheets.size(); ++i) if (externalStyleSheets.at(i).url == href) return; @@ -1970,7 +1992,7 @@ QList<QCss::Declaration> standardDeclarationForNode(const QTextHtmlParserNode &n 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) { + for (int i = 0; i < node.attributes.size(); i += 2) { const QString key = node.attributes.at(i); if (key.compare("href"_L1, Qt::CaseInsensitive) == 0 && !node.attributes.at(i + 1).isEmpty()) { @@ -2106,7 +2128,7 @@ QList<QCss::Declaration> standardDeclarationForNode(const QTextHtmlParserNode &n decl.d->propertyId = QCss::FontFamily; QList<QCss::Value> values; val.type = QCss::Value::String; - val.variant = QFontDatabase::systemFont(QFontDatabase::FixedFont).families().first(); + val.variant = QFontDatabase::systemFont(QFontDatabase::FixedFont).families().constFirst(); values << val; decl.d->values = values; decl.d->inheritable = true; @@ -2145,15 +2167,15 @@ QList<QCss::Declaration> QTextHtmlParser::declarationsForNode(int node) const int idx = 0; selector.styleSheets.resize((resourceProvider ? 1 : 0) - + externalStyleSheets.count() - + inlineStyleSheets.count()); + + externalStyleSheets.size() + + inlineStyleSheets.size()); if (resourceProvider) selector.styleSheets[idx++] = QTextDocumentPrivate::get(resourceProvider)->parsedDefaultStyleSheet; - for (int i = 0; i < externalStyleSheets.count(); ++i, ++idx) + for (int i = 0; i < externalStyleSheets.size(); ++i, ++idx) selector.styleSheets[idx] = externalStyleSheets.at(i).sheet; - for (int i = 0; i < inlineStyleSheets.count(); ++i, ++idx) + for (int i = 0; i < inlineStyleSheets.size(); ++i, ++idx) selector.styleSheets[idx] = inlineStyleSheets.at(i); selector.medium = resourceProvider ? resourceProvider->metaInformation(QTextDocument::CssMedia) : "screen"_L1; |