diff options
Diffstat (limited to 'src/quick/util/qquickstyledtext.cpp')
-rw-r--r-- | src/quick/util/qquickstyledtext.cpp | 193 |
1 files changed, 84 insertions, 109 deletions
diff --git a/src/quick/util/qquickstyledtext.cpp b/src/quick/util/qquickstyledtext.cpp index d967721b64..a595dbcbc2 100644 --- a/src/quick/util/qquickstyledtext.cpp +++ b/src/quick/util/qquickstyledtext.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick 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 <QStack> #include <QVector> @@ -45,6 +9,9 @@ #include <qmath.h> #include "qquickstyledtext_p.h" #include <QQmlContext> +#include <QtGui/private/qtexthtmlparser_p.h> + +Q_LOGGING_CATEGORY(lcStyledText, "qt.quick.styledtext") /* QQuickStyledText supports few tags: @@ -160,6 +127,13 @@ const QChar QQuickStyledTextPrivate::square(0x25a1); const QChar QQuickStyledTextPrivate::lineFeed(QLatin1Char('\n')); const QChar QQuickStyledTextPrivate::space(QLatin1Char(' ')); +namespace { +bool is_equal_ignoring_case(QStringView s1, QLatin1StringView s2) noexcept +{ + return s1.compare(s2, Qt::CaseInsensitive) == 0; +} +} + QQuickStyledText::QQuickStyledText(const QString &string, QTextLayout &layout, QList<QQuickStyledTextImgTag*> &imgTags, const QUrl &baseUrl, @@ -194,7 +168,7 @@ void QQuickStyledTextPrivate::parse() QStack<QTextCharFormat> formatStack; QString drawText; - drawText.reserve(text.count()); + drawText.reserve(text.size()); updateImagePositions = !imgTags->isEmpty(); @@ -214,31 +188,31 @@ void QQuickStyledTextPrivate::parse() hasSpace = true; } - if (rangeStart != drawText.length() && formatStack.count()) { + if (rangeStart != drawText.size() && formatStack.size()) { if (formatChanged) { QTextLayout::FormatRange formatRange; formatRange.format = formatStack.top(); formatRange.start = rangeStart; - formatRange.length = drawText.length() - rangeStart; + formatRange.length = drawText.size() - rangeStart; ranges.append(formatRange); formatChanged = false; - } else if (ranges.count()) { - ranges.last().length += drawText.length() - rangeStart; + } else if (ranges.size()) { + ranges.last().length += drawText.size() - rangeStart; } } - rangeStart = drawText.length(); + rangeStart = drawText.size(); ++ch; if (*ch == slash) { ++ch; if (parseCloseTag(ch, text, drawText)) { - if (formatStack.count()) { + if (formatStack.size()) { formatChanged = true; formatStack.pop(); } } } else { QTextCharFormat format; - if (formatStack.count()) + if (formatStack.size()) format = formatStack.top(); if (parseTag(ch, text, drawText, format)) { formatChanged = true; @@ -278,15 +252,15 @@ void QQuickStyledTextPrivate::parse() } if (textLength) appendText(text, textStart, textLength, drawText); - if (rangeStart != drawText.length() && formatStack.count()) { + if (rangeStart != drawText.size() && formatStack.size()) { if (formatChanged) { QTextLayout::FormatRange formatRange; formatRange.format = formatStack.top(); formatRange.start = rangeStart; - formatRange.length = drawText.length() - rangeStart; + formatRange.length = drawText.size() - rangeStart; ranges.append(formatRange); - } else if (ranges.count()) { - ranges.last().length += drawText.length() - rangeStart; + } else if (ranges.size()) { + ranges.last().length += drawText.size() - rangeStart; } } @@ -329,12 +303,12 @@ bool QQuickStyledTextPrivate::parseTag(const QChar *&ch, const QString &textIn, if (tagLength == 0) return false; auto tag = QStringView(textIn).mid(tagStart, tagLength); - const QChar char0 = tag.at(0); + const QChar char0 = tag.at(0).toLower(); if (char0 == QLatin1Char('b')) { if (tagLength == 1) { format.setFontWeight(QFont::Bold); return true; - } else if (tagLength == 2 && tag.at(1) == QLatin1Char('r')) { + } else if (tagLength == 2 && tag.at(1).toLower() == QLatin1Char('r')) { textOut.append(QChar(QChar::LineSeparator)); hasSpace = true; prependSpace = false; @@ -351,7 +325,7 @@ bool QQuickStyledTextPrivate::parseTag(const QChar *&ch, const QString &textIn, textOut.append(QChar::LineSeparator); hasSpace = true; prependSpace = false; - } else if (tag == QLatin1String("pre")) { + } else if (is_equal_ignoring_case(tag, QLatin1String("pre"))) { preFormat = true; if (!hasNewLine) textOut.append(QChar::LineSeparator); @@ -363,7 +337,7 @@ bool QQuickStyledTextPrivate::parseTag(const QChar *&ch, const QString &textIn, if (tagLength == 1) { format.setFontUnderline(true); return true; - } else if (tag == QLatin1String("ul")) { + } else if (is_equal_ignoring_case(tag, QLatin1String("ul"))) { List listItem; listItem.level = 0; listItem.type = Unordered; @@ -385,20 +359,20 @@ bool QQuickStyledTextPrivate::parseTag(const QChar *&ch, const QString &textIn, if (tagLength == 1) { format.setFontStrikeOut(true); return true; - } else if (tag == QLatin1String("strong")) { + } else if (is_equal_ignoring_case(tag, QLatin1String("strong"))) { format.setFontWeight(QFont::Bold); return true; } - } else if (tag == QLatin1String("del")) { + } else if (is_equal_ignoring_case(tag, QLatin1String("del"))) { format.setFontStrikeOut(true); return true; - } else if (tag == QLatin1String("ol")) { + } else if (is_equal_ignoring_case(tag, QLatin1String("ol"))) { List listItem; listItem.level = 0; listItem.type = Ordered; listItem.format = Decimal; listStack.push(listItem); - } else if (tag == QLatin1String("li")) { + } else if (is_equal_ignoring_case(tag, QLatin1String("li"))) { if (!hasNewLine) textOut.append(QChar(QChar::LineSeparator)); if (!listStack.isEmpty()) { @@ -438,20 +412,20 @@ bool QQuickStyledTextPrivate::parseTag(const QChar *&ch, const QString &textIn, } else if (ch->isSpace()) { // may have params. auto tag = QStringView(textIn).mid(tagStart, tagLength); - if (tag == QLatin1String("font")) + if (is_equal_ignoring_case(tag, QLatin1String("font"))) return parseFontAttributes(ch, textIn, format); - if (tag == QLatin1String("ol")) { + if (is_equal_ignoring_case(tag, QLatin1String("ol"))) { parseOrderedListAttributes(ch, textIn); return false; // doesn't modify format } - if (tag == QLatin1String("ul")) { + if (is_equal_ignoring_case(tag, QLatin1String("ul"))) { parseUnorderedListAttributes(ch, textIn); return false; // doesn't modify format } - if (tag == QLatin1String("a")) { + if (is_equal_ignoring_case(tag, QLatin1String("a"))) { return parseAnchorAttributes(ch, textIn, format); } - if (tag == QLatin1String("img")) { + if (is_equal_ignoring_case(tag, QLatin1String("img"))) { parseImageAttributes(ch, textIn, textOut); return false; } @@ -476,12 +450,12 @@ bool QQuickStyledTextPrivate::parseCloseTag(const QChar *&ch, const QString &tex if (tagLength == 0) return false; auto tag = QStringView(textIn).mid(tagStart, tagLength); - const QChar char0 = tag.at(0); + const QChar char0 = tag.at(0).toLower(); hasNewLine = false; if (char0 == QLatin1Char('b')) { if (tagLength == 1) return true; - else if (tag.at(1) == QLatin1Char('r') && tagLength == 2) + else if (tag.at(1).toLower() == QLatin1Char('r') && tagLength == 2) return false; } else if (char0 == QLatin1Char('i')) { if (tagLength == 1) @@ -495,7 +469,7 @@ bool QQuickStyledTextPrivate::parseCloseTag(const QChar *&ch, const QString &tex hasNewLine = true; hasSpace = true; return false; - } else if (tag == QLatin1String("pre")) { + } else if (is_equal_ignoring_case(tag, QLatin1String("pre"))) { preFormat = false; if (!hasNewLine) textOut.append(QChar::LineSeparator); @@ -506,10 +480,10 @@ bool QQuickStyledTextPrivate::parseCloseTag(const QChar *&ch, const QString &tex } else if (char0 == QLatin1Char('u')) { if (tagLength == 1) return true; - else if (tag == QLatin1String("ul")) { + else if (is_equal_ignoring_case(tag, QLatin1String("ul"))) { if (!listStack.isEmpty()) { listStack.pop(); - if (!listStack.count()) + if (!listStack.size()) textOut.append(QChar::LineSeparator); } return false; @@ -519,24 +493,24 @@ bool QQuickStyledTextPrivate::parseCloseTag(const QChar *&ch, const QString &tex hasNewLine = true; hasSpace = true; return true; - } else if (tag == QLatin1String("font")) { + } else if (is_equal_ignoring_case(tag, QLatin1String("font"))) { return true; } else if (char0 == QLatin1Char('s')) { if (tagLength == 1) { return true; - } else if (tag == QLatin1String("strong")) { + } else if (is_equal_ignoring_case(tag, QLatin1String("strong"))) { return true; } - } else if (tag == QLatin1String("del")) { + } else if (is_equal_ignoring_case(tag, QLatin1String("del"))) { return true; - } else if (tag == QLatin1String("ol")) { + } else if (is_equal_ignoring_case(tag, QLatin1String("ol"))) { if (!listStack.isEmpty()) { listStack.pop(); - if (!listStack.count()) + if (!listStack.size()) textOut.append(QChar::LineSeparator); } return false; - } else if (tag == QLatin1String("li")) { + } else if (is_equal_ignoring_case(tag, QLatin1String("li"))) { return false; } return false; @@ -556,16 +530,13 @@ void QQuickStyledTextPrivate::parseEntity(const QChar *&ch, const QString &textI while (!ch->isNull()) { if (*ch == QLatin1Char(';')) { auto entity = QStringView(textIn).mid(entityStart, entityLength); - if (entity == QLatin1String("gt")) - textOut += QChar(62); - else if (entity == QLatin1String("lt")) - textOut += QChar(60); - else if (entity == QLatin1String("amp")) - textOut += QChar(38); - else if (entity == QLatin1String("quot")) - textOut += QChar(34); - else if (entity == QLatin1String("nbsp")) - textOut += QChar(QChar::Nbsp); +#if QT_CONFIG(texthtmlparser) + const QString parsedEntity = QTextHtmlParser::parseEntity(entity); + if (!parsedEntity.isNull()) + textOut += parsedEntity; + else +#endif + qCWarning(lcStyledText) << "StyledText doesn't support entity" << entity; return; } else if (*ch == QLatin1Char(' ')) { auto entity = QStringView(textIn).mid(entityStart - 1, entityLength + 1); @@ -583,12 +554,12 @@ bool QQuickStyledTextPrivate::parseFontAttributes(const QChar *&ch, const QStrin QPair<QStringView,QStringView> attr; do { attr = parseAttribute(ch, textIn); - if (attr.first == QLatin1String("color")) { + if (is_equal_ignoring_case(attr.first, QLatin1String("color"))) { valid = true; - format.setForeground(QColor(attr.second.toString())); - } else if (attr.first == QLatin1String("size")) { + format.setForeground(QColor::fromString(attr.second)); + } else if (is_equal_ignoring_case(attr.first, QLatin1String("size"))) { valid = true; - int size = attr.second.toString().toInt(); + int size = attr.second.toInt(); if (attr.second.at(0) == QLatin1Char('-') || attr.second.at(0) == QLatin1Char('+')) size += 3; if (size >= 1 && size <= 7) @@ -611,7 +582,7 @@ bool QQuickStyledTextPrivate::parseOrderedListAttributes(const QChar *&ch, const QPair<QStringView,QStringView> attr; do { attr = parseAttribute(ch, textIn); - if (attr.first == QLatin1String("type")) { + if (is_equal_ignoring_case(attr.first, QLatin1String("type"))) { valid = true; if (attr.second == QLatin1String("a")) listItem.format = LowerAlpha; @@ -640,11 +611,11 @@ bool QQuickStyledTextPrivate::parseUnorderedListAttributes(const QChar *&ch, con QPair<QStringView,QStringView> attr; do { attr = parseAttribute(ch, textIn); - if (attr.first == QLatin1String("type")) { + if (is_equal_ignoring_case(attr.first, QLatin1String("type"))) { valid = true; - if (attr.second == QLatin1String("disc")) + if (is_equal_ignoring_case(attr.second, QLatin1String("disc"))) listItem.format = Disc; - else if (attr.second == QLatin1String("square")) + else if (is_equal_ignoring_case(attr.second, QLatin1String("square"))) listItem.format = Square; } } while (!ch->isNull() && !attr.first.isEmpty()); @@ -660,7 +631,7 @@ bool QQuickStyledTextPrivate::parseAnchorAttributes(const QChar *&ch, const QStr QPair<QStringView,QStringView> attr; do { attr = parseAttribute(ch, textIn); - if (attr.first == QLatin1String("href")) { + if (is_equal_ignoring_case(attr.first, QLatin1String("href"))) { format.setAnchorHref(attr.second.toString()); format.setAnchor(true); format.setFontUnderline(true); @@ -680,21 +651,21 @@ void QQuickStyledTextPrivate::parseImageAttributes(const QChar *&ch, const QStri if (!updateImagePositions) { QQuickStyledTextImgTag *image = new QQuickStyledTextImgTag; - image->position = textOut.length() + (trailingSpace ? 0 : 1); + image->position = textOut.size() + (trailingSpace ? 0 : 1); QPair<QStringView,QStringView> attr; do { attr = parseAttribute(ch, textIn); - if (attr.first == QLatin1String("src")) { - image->url = QUrl(attr.second.toString()); - } else if (attr.first == QLatin1String("width")) { + if (is_equal_ignoring_case(attr.first, QLatin1String("src"))) { + image->url = QUrl(attr.second.toString()); + } else if (is_equal_ignoring_case(attr.first, QLatin1String("width"))) { image->size.setWidth(attr.second.toString().toInt()); - } else if (attr.first == QLatin1String("height")) { + } else if (is_equal_ignoring_case(attr.first, QLatin1String("height"))) { image->size.setHeight(attr.second.toString().toInt()); - } else if (attr.first == QLatin1String("align")) { - if (attr.second.toString() == QLatin1String("top")) { + } else if (is_equal_ignoring_case(attr.first, QLatin1String("align"))) { + if (is_equal_ignoring_case(attr.second, QLatin1String("top"))) { image->align = QQuickStyledTextImgTag::Top; - } else if (attr.second.toString() == QLatin1String("middle")) { + } else if (is_equal_ignoring_case(attr.second, QLatin1String("middle"))) { image->align = QQuickStyledTextImgTag::Middle; } } @@ -706,25 +677,29 @@ void QQuickStyledTextPrivate::parseImageAttributes(const QChar *&ch, const QStri // to avoid a relayout later on. QUrl url = baseUrl.resolved(image->url); if (url.isLocalFile()) { - image->pix = new QQuickPixmap(context->engine(), url, QRect(), image->size); + image->pix.reset(new QQuickPixmap(context->engine(), url, QRect(), image->size)); if (image->pix && image->pix->isReady()) { image->size = image->pix->implicitSize(); } else { - delete image->pix; - image->pix = nullptr; + image->pix.reset(); } } } - imgWidth = image->size.width(); - image->offset = -std::fmod(imgWidth, spaceWidth) / 2.0; - imgTags->append(image); - + // Return immediately if img tag has invalid url + if (!image->url.isValid()) { + delete image; + qCWarning(lcStyledText) << "StyledText - Invalid base url in img tag"; + } else { + imgWidth = image->size.width(); + image->offset = -std::fmod(imgWidth, spaceWidth) / 2.0; + imgTags->append(image); + } } else { // if we already have a list of img tags for this text // we only want to update the positions of these tags. QQuickStyledTextImgTag *image = imgTags->value(nbImages); - image->position = textOut.length() + (trailingSpace ? 0 : 1); + image->position = textOut.size() + (trailingSpace ? 0 : 1); imgWidth = image->size.width(); image->offset = -std::fmod(imgWidth, spaceWidth) / 2.0; QPair<QStringView,QStringView> attr; |