How to get HTML text without meta information from component QTextDocument

Viewed 320

Description

I created a TextArea component in QML, and similar to this example, I created a DocumentHandler class based on a pointer to a QQuickTextDocument, which is taken through the textDocument property. I need this in order to be able to format the text, that is, make it bold, underlined, italic, strikeOut etc.

What I need

I need to get a text where the formatted parts will be presented as HTML tags.

e.g. Bold text ultimately I would like to get in the form <b>Bold text</b>. Or for example Bold and italic text I would like to get in the form <b><i>Bold and italic text</i></b> (the order in which the tags are placed does not matter).

What I tried

I tried to use the toHtml() function, but this function does not suit me because:

  1. It generates a lot of unnecessary information that I don't need. For example for Bold text it returned the following result:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body style=" font-family:'Roboto'; font-size:14px; font-weight:400; font-style:normal;">
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Bold text</span></p></body></html>
  1. The usual tags that I need to represent the text (<b>, <i> etc.), this function is formed in the form of the style attribute of the <span> tag. So it changes the bold with this line: <span style=" font-weight:600;">.
1 Answers

Description

If I understood correctly, at the moment there is no way to get formatted text with HTML tags without meta information that is generated by the QTextDocument using the toHtml() function. Therefore, I decided to manually do this work using the QTextCursor class.

Code

I have a structure that provides information about tag:

struct Tag
{
    Tag(const QString& openTag,
        const QString& closeTag,
        const std::function<bool(const QTextCursor& cursor)>& canBeOpened);

    QString getOpenTag() const;
    QString getCloseTag() const;
    bool isOpened() const;
    bool isClosed() const;
    bool canBeOpened(const QTextCursor& cursor) const;
    void open();
    void close();

private:
    std::function<bool(const QTextCursor&)> m_canBeOpened;
    QString m_openTag;
    QString m_closeTag;
    bool m_isOpened{ false };
    bool m_isClosed{ true };
};

And I have a std::vector of such structures which I initialize as follows:

m_tags{ { "<b>", "</b>", [](const QTextCursor& cursor) { return cursor.charFormat().fontWeight() == QFont::Bold; } },
        { "<i>", "</i>", [](const QTextCursor& cursor) { return cursor.charFormat().fontItalic(); } },
        { "<u>", "</u>", [](const QTextCursor& cursor) { return cursor.charFormat().fontUnderline(); } },
        { "<s>", "</s>", [](const QTextCursor& cursor) { return cursor.charFormat().fontStrikeOut(); } } }

And the most important thing is the getFormattedText() function that uses this vector of Tag objects to return the formatted text. The main idea is to manually place tags in plain text, that is, the opening tag is placed where the formatting begins, and the closing tag is where it ends. Information about where in the text what formatting is used can be taken from the QTextCursor class, which object we can create based on the QTextDocument class. As a result, we have the following function:

QString getFormattedText()
{
    QTextCursor cursor{ textCursor() };
    if (!cursor.isNull())
    {
        QString result{ cursor.document()->toPlainText() };
        for (int i{}, offset{}; i < cursor.document()->characterCount(); ++i)
        {
            cursor.setPosition(i)
            for (auto& tag : m_tags)
            {
                if (tag.canBeOpened(cursor))
                {
                    if (!tag.isOpened())
                    {
                        result.insert(i - (i > 0 ? 1 : 0) + offset, tag.getOpenTag());
                        offset += tag.getOpenTag().size();
                        tag.open();
                    }
                }
                else if (!tag.isClosed())
                {
                    result.insert(i - (i > 0 ? 1 : 0) + offset, tag.getCloseTag());
                    offset += tag.getCloseTag().size();
                    tag.close();
                }
            }
        }
        for (int i = m_tags.size() - 1; i >= 0; --i)
        {
            if (!m_tags[i].isClosed())
            {
                result.insert(result.size(), m_tags[i].getCloseTag());
                m_tags[i].close();
            }
        }
        return result;
    }
    return {};
}

Bold and italic text will look like this <b><i>Bold and italic text</i></b>

Related