富文本文档结构

文本文档由QTextDocument类表示,该类包含有关文档的内部表示、结构的信息,并跟踪修改以提供撤销/重做功能。

文本文档的结构化表示以文本块、框架、表格和其他对象的层次结构展示其内容。这给文档提供了逻辑结构,并描述了其内容将如何显示。通常,框架和表格用于分组其他结构,而文本块包含实际的文本信息。

新元素可以通过程序化方式使用QTextCursor或使用编辑器小部件(如QTextEdit)插入到文档中。当元素被创建时,可以为它们指定特定格式;否则,它们将为元素采用光标的当前格式。

基本结构

文档的“顶级”可能像下面这样填充。每个文档始终包含一个根框架,且该框架至少包含一个文本块。

对于包含一些文本内容的文档,根框架通常包含一系列块和其他元素。

文档中的框架和表格序列总是由文本块隔开,即使文本块不包含任何信息也是如此。这确保了新元素可以始终插入到现有结构之间。

在本章中,我们将查看在富文本文档中使用的每个结构元素,概述其特性和用途,并展示如何检查它们的内容。文档编辑在QTextCursor接口中描述。

富文本文档

QTextDocument对象包含构建富文本文档所需的所有信息。文本文档可以通过两种互补方式进行访问:作为编辑器使用的线性缓冲区,以及布局引擎有用的对象层次结构。在层次文档模型中,对象通常对应于诸如框架、表格和列表之类的视觉元素。在较低级别,这些元素描述了诸如文本样式和对齐等属性。文档的线性表示用于编辑和操作文档内容。

尽管QTextEdit使得展示和编辑富文本变得容易,但文档也可以独立于任何编辑器小部件使用,例如:

QTextDocument *newDocument = new QTextDocument;

或者,它们可以从不存在的编辑器中提取

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

这种灵活性使得应用能够在不产生多个编辑器小部件的开销或不要求将文档存储在某种中间格式的情况下处理多个富文本文档。

一个空文档包含一个根框架,而该框架本身包含一个单独的空文本块。框架在文档的各个部分之间提供逻辑分隔,同时也具有确定其呈现方式的属性。表格是一种特殊的框架类型,由多个单元格组成,单元格排成行和列,每个单元格都可以包含进一步的结构和文本。表格提供了管理和布局功能,允许创建具有灵活配置的单元格。

文本块包含文本片段,每个片段都指定了文本和字符格式信息。文本属性在字符级别和块级别上定义。在字符级别,可以指定字体家族、文本颜色和字体重量等属性。块级别的属性控制文本的高级外观和行为,例如文本流的方向、对齐方式和背景颜色。

文档结构不是直接操作的。编辑是通过基于光标的界面进行的。文本光标界面自动将新文档元素插入根框架,并在必要时以空块填充。

我们以以下方式获取根框架

    QTextDocument *editorDocument = editor->document();
    QTextFrame *root = editorDocument->rootFrame();

在导航文档结构时,从根框架开始是有用的,因为它提供了访问整个文档结构的能力。

文档元素

富文本文档通常由段落、框架、表格和列表等常见元素组成。这些元素由QTextDocument通过QTextBlockQTextFrameQTextTableQTextList类表示。与其他文档元素不同,图像由特别格式化的文本片段表示,这使得它们可以与周围文本格式内联放置。

文档中的基本结构构建块是QTextBlockQTextFrame。块本身包含某些富文本片段(QTextFragment),但这些不会直接影响文档的高级结构。

可以组合其他文档元素的元素通常是QTextObject的子类,并分为两类:组合文本块的子类是QTextBlockGroup,而组合框架和其他元素的子类是QTextFrame

文本块

文本块由QTextBlock类提供。

文本块将具有不同字符格式的文本片段组合在一起,并用于表示文档中的段落。每个块通常包含多个具有不同样式的文本片段。当文本插入到文档中时创建片段,当文档被编辑时添加更多片段。文档拆分、合并和删除片段以高效地表示块中不同样式的文本。

可以使用QTextBlock::iterator examine the fragments within a given block by traversing the block's internal structure

    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();
    }

当您只想从文档中提取富文本时,该方法非常有用,因为它忽略了框架、表格和其他类型的结构。

QTextBlock提供了比较操作符,使其更容易操作块:operator==()和operator!=()用于测试两个块是否相同,而operator<()用于确定在文档中哪个先。

框架

框架由QTextFrame类提供。

文本框架将一组文本块和子框架组合在一起,创建大于段落的文档结构。框架的格式指定了其在页面上的渲染和位置。框架可以是插入到文本流中,或者悬浮在页面的左侧或右侧。每个文档包含一个包含所有其他文档元素的根框架。因此,除了根框架之外的所有框架都有一个父框架。

由于文本块用于分隔其他文档元素,每个框架将始终包含至少一个文本块和零个或多个子框架。我们可以通过使用QTextFrame::iterator遍历框架的子元素来检查框架的内容

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

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

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

请注意,迭代器选择框架和块,因此必须检查它引用的是哪个。这使得我们可以在框架的基础上导航文档结构,同时在需要时访问文本块。QTextBlock::iteratorQTextFrame::iterator类可以互补地使用,以从文档中提取所需的结构。

表格

表格由QTextTable类提供。

表格是由行列排列的单元格集合。每个表格单元格都是一个具有自己字符格式的文档元素,但它还可以包含其他元素,如框架和文本块。表格单元格在构建表格时自动创建,或者在添加额外行或列时创建。它们也可以在表格之间移动。

QTextTableQTextFrame的子类,因此表格在文档结构中类似于框架。对于我们在文档中遇到的每个框架,我们可以测试它是否表示表格,并以不同的方式处理它

    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(childTable);
            else
                processFrame(childFrame);

        } else if (childBlock.isValid()) {
            processBlock(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);
    }

由于 QTextListQTextBlockGroup 的子类,因此它不会将列表项作为子元素分组,而是提供各种管理它们的函数。这意味着我们在遍历文档时找到的任何文本块实际上可能是一个列表项。我们可以使用以下代码确保正确识别列表项

    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);
        }
    }

可以通过遍历包含图片的文本块中的片段来找到表示图片的片段。

© 2024 Qt 公司有限公司。此处包含的文档贡献为各自所有者的版权。此处提供的文档是在自由软件基金会的许可下提供的,即 GNU 自由文档许可协议 1.3 版本。Qt 及其相应标志是芬兰及其它国家 Qt 公司的商标。所有其他商标均为各自所有者的财产。