Spec-Zone.ru › Qt 5.15

Структура документа Rich Text

Текстовые документы представлены классом QTextDocument, который содержит информацию о внутреннем представлении документа, его структуре и отслеживает изменения для обеспечения функций отмены/повтора.

Структурированное представление текстового документа представляет его содержимое в виде иерархии текстовых блоков, фреймов, таблиц и других объектов. Эти элементы обеспечивают логическую структуру документа и описывают, как будет отображаться их содержимое. Обычно фреймы и таблицы используются для группирования других структур, в то время как текстовые блоки содержат фактическую текстовую информацию.

Новые элементы создаются и вставляются в документ программно с помощью QTextCursor или с помощью виджета редактора, например, QTextEdit. Элементам можно задать определенный формат при их создании; в противном случае они принимают текущий формат курсора для элемента.

Основная структура

«Верхний уровень» документа может быть заполнен так, как показано. Каждый документ всегда содержит корневой фрейм, а он всегда содержит как минимум один текстовый блок.

Для документов с некоторым текстовым содержимым корневой фрейм обычно содержит последовательность блоков и других элементов.

Последовательности фреймов и таблиц всегда разделяются текстовыми блоками в документе, даже если текстовые блоки не содержат никакой информации. Это гарантирует, что новые элементы всегда можно вставить между существующими структурами.

В этой главе мы рассмотрим каждый из структурных элементов, используемых в документе Rich Text, определим их особенности и использование, и покажем, как просмотреть их содержимое. Редактирование документов описано в Интерфейсе QTextCursor.

Документы Rich Text

QTextDocument объекты содержат всю необходимую информацию для построения документов Rich Text. Текстовые документы можно получить двумя взаимодополняющими способами: как линейный буфер для использования редакторами и как иерархическую структуру объектов, которая полезна для движков верстки. В иерархической модели документа объекты обычно соответствуют визуальным элементам, таким как фреймы, таблицы и списки. На более низком уровне эти элементы описывают такие свойства, как стиль текста и выравнивание. Линейное представление документа используется для редактирования и обработки содержимого документа.

Хотя QTextEdit упрощает отображение и редактирование Rich Text, документы также могут использоваться независимо от любого виджета редактора, например:

QTextDocument *newDocument = new QTextDocument;

В качестве альтернативы их можно извлечь из существующего редактора:

QTextEdit *editor = new QTextEdit;
QTextDocument *editorDocument = editor->document();

Эта гибкость позволяет приложениям обрабатывать несколько документов Rich Text без избыточных накладных расходов множества виджетов редактора или без необходимости хранения документов в некотором промежуточном формате.

Пустой документ содержит корневой фрейм, который сам содержит один пустой текстовый блок. Фреймы обеспечивают логическое разделение частей документа, но также имеют свойства, определяющие, как они будут отображаться при рендеринге. Таблица — это специализированный тип фрейма, состоящий из нескольких ячеек, расположенных в строки и столбцы, каждая из которых может содержать дополнительную структуру и текст. Таблицы предоставляют функции управления и верстки, которые позволяют создавать гибкие конфигурации ячеек.

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

Структура документа не изменяется напрямую. Редактирование выполняется через интерфейс на основе курсора. Интерфейс текстового курсора автоматически вставляет новые элементы документа в корневой фрейм и гарантирует, что он заполнен пустыми блоками по мере необходимости.

Мы получаем корневой фрейм следующим образом:

    QTextDocument *textDocument;
    QTextFrame *root = textDocument->rootFrame();

При навигации по структуре документа полезно начинать с корневого фрейма, потому что он обеспечивает доступ ко всей структуре документа.

Элементы документа

Документы Rich Text обычно состоят из общих элементов, таких как абзацы, фреймы, таблицы и списки. Они представлены в QTextDocument классами QTextBlock, QTextFrame, QTextTable и QTextList. В отличие от других элементов документа, изображения представлены специально отформатированными фрагментами текста. Это позволяет размещать их отформатированными в строку с окружающим текстом.

Основными структурными строительными блоками в документах являются QTextBlock и QTextFrame. Сами блоки содержат фрагменты Rich Text (QTextFragment), но они не напрямую влияют на общую структуру документа.

Элементы, которые могут объединять другие элементы документа, обычно являются подклассами QTextObject и делятся на две категории: элементы, которые объединяют текстовые блоки, являются подклассами QTextBlockGroup, а элементы, которые объединяют фреймы и другие элементы, являются подклассами QTextFrame.

Текстовые блоки

Текстовые блоки предоставляются классом QTextBlock.

Текстовые блоки объединяют фрагменты текста с разными форматами символов и используются для представления абзацев в документе. Каждый блок обычно содержит несколько фрагментов текста с разными стилями. Фрагменты создаются при вставке текста в документ, и больше их добавляется при редактировании документа. Документ разбивает, объединяет и удаляет фрагменты, чтобы эффективно представлять разные стили текста в блоке.

Фрагменты внутри данного блока можно просмотреть, используя итератор QTextBlock::iterator для обхода внутренней структуры блока:

    QTextBlock::iterator it;
    for (it = currentBlock.begin(); !(it.atEnd()); ++it) {
        QTextFragment currentFragment = it.fragment();
        if (currentFragment.isValid())
            processFragment(currentFragment);
    }

Блоки также используются для представления элементов списка. В результате блоки могут определять свои собственные форматы символов, которые содержат информацию об оформлении блока, например, тип маркеров, используемых для элементов списка. Форматирование самого блока описывается классом QTextBlockFormat и описывает такие свойства, как выравнивание текста, отступы и цвет фона.

Хотя данный документ может содержать сложные структуры, как только у нас есть ссылка на действительный блок в документе, мы можем перемещаться между каждым из текстовых блоков в порядке их написания:

    QTextBlock currentBlock = textDocument->begin();

    while (currentBlock.isValid()) {
        processBlock(currentBlock);
        currentBlock = currentBlock.next();
    }

Этот метод полезен, когда вы хотите извлечь только Rich Text из документа, поскольку он игнорирует фреймы, таблицы и другие типы структур.

QTextBlock предоставляет операторы сравнения, которые упрощают работу с блоками: operator==() и operator!=() используются для проверки на совпадение двух блоков, а operator<() используется для определения, какой из них встречается раньше в документе.

Фреймы

Фреймы предоставляются классом QTextFrame.

Текстовые фреймы объединяют блоки текста и дочерние фреймы, создавая структуры документов, которые больше, чем абзацы. Формат фрейма определяет, как он отображается и позиционируется на странице. Фреймы либо вставляются в поток текста, либо они плавают слева или справа от страницы. Каждый документ содержит корневой фрейм, который содержит все другие элементы документа. В результате все фреймы, кроме корневого, имеют родительский фрейм.

Поскольку текстовые блоки используются для разделения других элементов документа, каждый фрейм всегда будет содержать как минимум один текстовый блок и ноль или более дочерних фреймов. Мы можем проверить содержимое фрейма, используя итератор QTextFrame::iterator для обхода дочерних элементов фрейма:

    QDomElement frameElement = ...

    QTextFrame::iterator it;
    for (it = frame->begin(); !(it.atEnd()); ++it) {

        QTextFrame *childFrame = it.currentFrame();
        QTextBlock childBlock = it.currentBlock();

        if (childFrame)
            processFrame(frameElement, childFrame);
        else if (childBlock.isValid())
            processBlock(frameElement, childBlock);
    }

Обратите внимание, что итератор выбирает как фреймы, так и блоки, поэтому необходимо проверить, к какому типу он относится. Это позволяет нам перемещаться по структуре документа на основе фреймов, но при необходимости все еще получать доступ к текстовым блокам. Классы QTextBlock::iterator и QTextFrame::iterator могут использоваться в дополнение друг к другу для извлечения необходимой структуры из документа.

Таблицы

Таблицы предоставляются классом QTextTable.

Таблицы представляют собой коллекции ячеек, расположенных в строки и столбцы. Каждая ячейка таблицы — это элемент документа со своим форматом символов, но она также может содержать другие элементы, такие как фреймы и текстовые блоки. Ячейки таблицы автоматически создаются при построении таблицы или при добавлении дополнительных строк или столбцов. Они также могут перемещаться между таблицами.

QTextTable является подклассом QTextFrame, поэтому таблицы обрабатываются как фреймы в структуре документа. Для каждого фрейма, с которым мы сталкиваемся в документе, мы можем проверить, представляет ли он собой таблицу, и обработать его другим способом:

    QDomElement frameElement = ...

    QTextFrame::iterator it;
    for (it = frame->begin(); !(it.atEnd()); ++it) {

        QTextFrame *childFrame = it.currentFrame();
        QTextBlock childBlock = it.currentBlock();

        if (childFrame) {
            QTextTable *childTable = qobject_cast<QTextTable*>(childFrame);

            if (childTable)
                processTable(frameElement, childTable);
            else
                processFrame(frameElement, childFrame);

        } else if (childBlock.isValid())
            processBlock(frameElement, childBlock);
    }

Ячейки в существующей таблице можно просмотреть, проитерировавшись по строкам и столбцам.

    for (int row = 0; row < table->rows(); ++row) {
        for (int column = 0; column < table->columns(); ++column) {
            QTextTableCell tableCell = table->cellAt(row, column);
            processTableCell(tableCell);
        }
    }

Списки

Списки предоставляются классом QTextList.

Списки представляют собой последовательности текстовых блоков, которые отформатированы обычным способом, но также обеспечивают стандартные украшения списков, такие как маркеры и нумерованные элементы. Списки могут быть вложенными и будут отступом, если формат списка указывает ненулевой отступ.

Мы можем сослаться на каждый элемент списка по его индексу в списке:

    for (int index = 0; index < list->count(); ++index) {
        QTextBlock listItem = list->item(index);
        processListItem(listItem);
    }

Поскольку QTextList является подклассом QTextBlockGroup, он не группирует элементы списка как дочерние элементы, а вместо этого предоставляет различные функции для их управления. Это означает, что любой текстовый блок, который мы найдем при обходе документа, может фактически быть элементом списка. Мы можем гарантировать, что элементы списка будут правильно идентифицированы, используя следующий код:

    QTextFrame::iterator it;
    for (it = frame->begin(); !(it.atEnd()); ++it) {

        QTextBlock block = it.currentBlock();

        if (block.isValid()) {

            QTextList *list = block.textList();

            if (list) {
                int index = list->itemNumber(block);
                processListItem(list, index);
            }
        }
    }

Изображения

Изображения в QTextDocument представляются фрагментами текста, которые ссылаются на внешние изображения через механизм ресурсов. Изображения создаются с помощью интерфейса курсора и могут быть изменены позже путем изменения формата символов фрагмента текста изображения:

    if (fragment.isValid()) {
        QTextImageFormat newImageFormat = fragment.charFormat().toImageFormat();

        if (newImageFormat.isValid()) {
            newImageFormat.setName(":/images/newimage.png");
            QTextCursor helper = cursor;

            helper.setPosition(fragment.position());
            helper.setPosition(fragment.position() + fragment.length(),
                                QTextCursor::KeepAnchor);
            helper.setCharFormat(newImageFormat);
        }
    }

Фрагмент, представляющий изображение, можно найти, проитерировавшись по фрагментам в текстовом блоке, содержащем изображение.

© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.14/richtext-structure.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API