Qt - Как подсчитать и измерить строки в QTextDocument?

В одном из моих проектов я создал QTextDocument, которому принадлежит текст, который мне нужно нарисовать. Текст представляет собой текст в формате HTML, обернутый словами, и он должен быть нарисован в прямоугольной области, ширину которой я знаю. Его содержание также никогда не будет превышать абзаца.

Текстовый документ создается следующим образом:

// create and configure the text document to measure
QTextDocument textDoc;
textDoc.setHtml(text);
textDoc.setDocumentMargin(m_TextMargin);
textDoc.setDefaultFont(m_Font);
textDoc.setDefaultTextOption(m_TextOption);
textDoc.setTextWidth(m_Background.GetMessageWidth(size().width()));

и вот пример текста, который я хочу нарисовать:

Ceci est un texte <img src=\"Resources/1f601.svg\" width=\"24\" height=\"24\"> avec <img src=\"Resources/1f970.svg\" width=\"24\" height=\"24\"> une <img src=\"Resources/1f914.svg\" width=\"24\" height=\"24\"> dizaine <img src=\"Resources/1f469-1f3fe.svg\" width=\"24\" height=\"24\"> de <img src=\"Resources/1f3a8.svg\" width=\"24\" height=\"24\"> mots. Pour voir comment la vue réagit.

Изображения представляют собой изображения SVG, полученные из ресурсов qml.

Чтобы выполнить несколько операций во время отрисовки текста, мне нужно знать, сколько строк будет отрисовано после применения переноса слов, а также высоту любой строки в тексте с переносом слов.

Я пытался искать в функциях, предоставляемых текстовым документом, а также в функциях, предоставляемых в QTextBLock, QTextFragment и QTextCursor. Я пробовал несколько подходов, таких как перебор символов с помощью курсора и подсчет строк или подсчет каждого фрагмента в блоке. К сожалению, ни одна из них не сработала: все функции всегда считают 1 строку или просто не работают.

Вот пример кода, который я уже пробовал, но безуспешно:

// FAILS - always return 1
int lineCount = textDoc.lineCount()

// FAILS - always return 1
int lineCount = textDoc.blockCount()

// FAILS - return the whole text height, not a particular line height at index
int lineHeight = int(textDoc.documentLayout()->blockBoundingRect(textDoc.findBlockByNumber(lineNb)).height());
// get the paragraph (there is only 1 paragraph in the item text document
QTextBlock textBlock = textDoc.findBlockByLineNumber(lineNb);

int blockCount = 0;

for (QTextBlock::iterator it = textBlock.begin(); it != textBlock.end(); ++it)
{
    // FAILS - fragments aren't divided by line, e.g an image will generate a fragment
    QString blockText = it.fragment().text();
    ++blockCount;
}

return blockCount;
QTextCursor cursor(&textDoc);

int lineCount = 0;

cursor.movePosition(QTextCursor::Start);

// FAILS - movePosition() always return false
while (cursor.movePosition(QTextCursor::Down))
    ++lineCount;

Я не могу понять, что я делаю неправильно, и почему все мои подходы терпят неудачу.

Итак, мои вопросы:

  1. Как подсчитать количество строк, содержащихся в моем перенесенном по словам документе
  2. Как я могу измерить высоту строки в моем словном переносе документа
  3. Не работает ли функция текстового документа из-за формата html? Если да, что мне делать, чтобы достичь своих целей в таком контексте?

ПРИМЕЧАНИЕ Я знаю, как измерить высоту всего текста. Однако, поскольку каждая высота строки может быть разной, я не могу просто разделить всю высоту текста на строки, поэтому для меня это неприемлемое решение.


person Jean-Milost Reymond    schedule 03.04.2020    source источник


Ответы (1)


Наконец-то я нашел способ решить свою проблему. Текстовый документ нельзя использовать напрямую для измерения отдельных строк, вместо этого для этой цели следует использовать макет, как объясняется в следующем сообщении: https://forum.qt.io/topic/113275/how-дляподсчетаиизмерениястроквqtextdocument

Таким образом, следующий код является решением:

//---------------------------------------------------------------------------
int getLineCount(const QString& text) const
{
    // create and configure the text document to measure
    QTextDocument textDoc;
    textDoc.setHtml(text);
    textDoc.setDocumentMargin(m_TextMargin);
    textDoc.setDefaultFont(m_Font);
    textDoc.setDefaultTextOption(m_TextOption);
    textDoc.setTextWidth(m_Background.GetMessageWidth(size().width()));

    // this line is required to force the document to create the layout, which will then be used
    //to count the lines
    textDoc.documentLayout();

    // the document should at least contain one block
    if (textDoc.blockCount() < 1)
        return -1;

    int lineCount = 0;

    // iterate through document paragraphs (NOTE normally the message item should contain only 1 paragraph
    for (QTextBlock it = textDoc.begin(); it != textDoc.end(); it = it.next())
    {
        // get the block layout
        QTextLayout* pBlockLayout = it.layout();

        // should always exist, otherwise error
        if (!pBlockLayout)
            return -1;

        // count the block lines
        lineCount += pBlockLayout->lineCount();
    }

    return lineCount;
}
//---------------------------------------------------------------------------
int measureLineHeight(const QString& text, int lineNb, int blockNb) const
{
    // create and configure the text document to measure
    QTextDocument textDoc;
    textDoc.setHtml(text);
    textDoc.setDocumentMargin(m_TextMargin);
    textDoc.setDefaultFont(m_Font);
    textDoc.setDefaultTextOption(m_TextOption);
    textDoc.setTextWidth(m_Background.GetMessageWidth(size().width()));

    // this line is required to force the document to create the layout, which will then be used
    //to count the lines
    textDoc.documentLayout();

    // check if block number is out of bounds
    if (blockNb >= textDoc.blockCount())
        return -1;

    // get text block and its layout
    QTextBlock   textBlock = textDoc.findBlockByNumber(blockNb);
    QTextLayout* pLayout   = textBlock.layout();

    if (!pLayout)
        return -1;

    // check if line number is out of bounds
    if (lineNb >= pLayout->lineCount())
        return -1;

    // get the line to measure
    QTextLine textLine = pLayout->lineAt(lineNb);

    return textLine.height();
}
//---------------------------------------------------------------------------
person Jean-Milost Reymond    schedule 06.04.2020