最近使用 Qt 做一个离线博客编辑器,因而用到了 Qt 的富文本处理。参考 Qt 的文档,记录下 Qt 的富文本处理的相关技术。文档地址是 http://doc.qt.nokia.com/4.7/richtext.html,本文不是文档的准确翻译,但是内容和文档是基本一致的。

新版本 Qt 使用 QTextDocument 类作为富文本处理的中心类。相比之下,老版本则是使用基于文本的标记语言。

现在,Qt 操作文档的接口可以分成两类:基于光标的接口(cursor-based interface)用于编辑,只读的层次接口(read-only hierarchical interface)用于提供文档结构的概览。基于光标的编辑的主要优点是,能够自然地模拟用户使用编辑器进行编辑的过程,不会丢失文档的底层结构;而只读的层次接口则有利于对于文档检索和导出。

富文本文档结构

富文本文档由 QTextDocument 类描述。这个类包含了文档的内部表示、结构,并且内置了 redo/undo 等操作的支持。

文档的结构化表示描述了它包含的文本块(text block)、框架(frame)、表格(table)以及其他元素的层次化信息。这提供了文档的逻辑结构以及展现其内容的方式。一般的,框架和表格用于组织其他结构,而文本块则包含真正的文本信息。

新元素的创建和插入可以通过使用 QTextCursor 以编程的方式实现,或者通过 QTextEdit 以用户可视化编辑的方式实现。元素可以在创建时指定一个特定的样式,或者是直接使用当前光标所在位置的样式。

下图给出了文档结构的示例。

文档的基本结构是:文档的“顶层”决定显示的方式布局。每一个文档都包括一个根框架(root frame),以及至少一个文本块。对于含有多种文本内容的文档,根框架通常会包含一系列的块和其他元素。在文档中,框架和表格的顺序由文本块分隔开。有时候这些文本块根本没有内容。这保证了新元素总是能够插进原有结构之间。

富文本文档

QTextDocument 类包含了富文本文档的所有信息。前面说过,文档可以用两种方式访问:方便编辑器使用的线性缓存(linear buffer)和方便布局引擎使用的对象层次(object hierarchy)。在层次化文档模型中,对象用来描述可视元素,如框架、表格和列表。在更低的层次上,这些元素都有自己的描述属性,如文本风格和对齐方式。文档的线性描述则用于编辑和维护文档内容。

虽然 QTextEdit 提供了方便的富文本显示和编辑的功能,但文档也可以脱离任何一种编辑组件独立使用。例如:

  1. QTextDocument *newDocument = new QTextDocument;

另外,也可以通过已有的文本组件获得:

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

这种灵活性使得应用程序能够同时操作多个文档,而不必包含多个文档组件,也不比要求文档必须存储为某种中间格式。

一个空的文档包含一个根框架,这个框架包含一个空的文本块。框架提供不同文档部分的逻辑分割,同时也提供了在渲染时如何显示的属性。一个表格就是一个特化的框架,包含分布在不同行和列的多个单元。每个单元都能够包含更多的结构和文本。表格提供了灵活配置单元的管理和布局的特性。

文本块包含文本片段。每一个文本片段都有特定的文本和字符格式信息。文本属性在字符级别和块级别定义。在字符级别可以指定字体、颜色和大小。在块级别可以指定更高一级的行为,例如文本流方向、对齐方式和背景色。

文档结构并不是直接维护的,而是需要通过基于光标的接口进行编辑。文档光标接口会自动向根框架插入新的文档元素,并且确保这个元素在必要时有一个适合的空块。

我们可以通过以下方式访问到根框架:

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

当需要进行文档结构导航时,有时候可以从根框架开始。因为根框架提供了访问整个文档结构的能力。

文档元素

富文本文档通常包括一些通用的元素,例如段落、框架、表格和列表。这些在 QTextDocument 类中分别使用 QTextBlock,QTextFrame,QTextTable 和 QTextList 描述。不同于文档的其他元素,图片使用一种特殊的文本片段描述,这使得图片可以同普通文本混排。

文档的基本构建单位是 QTextBlock 和 QTextFrame。块本身就包含文本片段(QTextFragment),但是这不会直接影响到高层次的文档结构。

能够对其他文档元素分组的是 QTextObject 的子类。这些元素被分为两种类型:对文本块分组的是 QTextBlockGroup 的子类,对文本片段和其他元素分组的是 QTextFrame 的子类。

文本块

文本块由 QTextBlock 类提供。

文本块可以将具有不同字符样式的文本分组,用于表示文档段落。典型的文本块具有若干个不同样式的文本片段。当文本插入文档时,文本块被创建。在对文档进行编辑时,会增加更多的文本块。在块中,文档通过分割、合并、删除片段,有效地表现不同样式的文本。

一个文本块中的片段可以通过 QTextBlock::iterator 遍历:

  1. QTextBlock::iterator it;
  2. for (it = currentBlock.begin(); !(it.atEnd()); ++it) {
  3. QTextFragment currentFragment = it.fragment();
  4. if (currentFragment.isValid())
  5. processFragment(currentFragment);
  6. }
  7. }

文本块也可以用来表现列表项。因此,块能够在它们自己的字符样式包含块级样式信息,例如列表项的符号等。块自己的样式由 QTextBlockFormat 描述,包括文本对齐方式,缩进和背景色。

虽然一个给定的文档可能包含复杂的结构,但是只要我们有一个文档中的文本块,我们就可以通过这个文本块对文档中所有文本块以编写顺序进行导航:

  1. QTextBlock currentBlock = textDocument->begin();
  2. while (currentBlock.isValid()) {
  3. processBlock(currentBlock);
  4. currentBlock = currentBlock.next();
  5. }

当你需要导出文档中所有富文本内容时,这个方法就十分有用。因为它会忽略框架、表格以及其他文档结构。

QTextBlock 提供了比较运算符使操作更为简便:operator==() 和 operator!=() 用于比较两个块是否相同;operator<() 用于判断哪一个块在文档中的出现顺序靠前。

框架

框架由 QTextFrame 类提供。

文本框架用于组织文本块和子框架。这是一种比段落更大一级的文档结构。框架的格式决定它如何被显示和在页面中的位置。框架不是被插入文本流,就是浮在页面的左侧或者右侧。每一个文档都有一个根框架,包含了文档的所有结构。因此,除根框架之外,所有框架都有父框架。

既然文本块用于分割文档结构,那么,每一个框架都将至少包含一个文本块,零个或者多个子框架。我们可以通过 QTextFrame::iterator 来遍历所有子元素:

  1. QTextFrame::iterator it;
  2. for (it = frame->begin(); !(it.atEnd()); ++it) {
  3. QTextFrame *childFrame = it.currentFrame();
  4. QTextBlock childBlock = it.currentBlock();
  5. if (childFrame)
  6. processFrame(frameElement, childFrame);
  7. else if (childBlock.isValid())
  8. processBlock(frameElement, childBlock);
  9. }

注意,上面的代码,遍历器同时选出了文本块和框架,因此需要判断究竟是哪种元素。这能够让我们一个框架一个框架地导航文档,同时在需要的时候能够很方便地取出文本块。

QTextBlock::iterator 和 QTextFrame::iterator 两个类能够互补地取出文档中所需的结构。

表格

表格由 QTextTable 类提供。

表格是一个分布在行和列的单元的集合。每一个表格单元都是一个文档元素,拥有自己的字符样式,能够包含其他元素,例如框架和文本块。在表格构建,或者增加行或者列时,表格单元被自动创建。表格单元也可以在两个表格之间移动。

QTextTable 是 QTextFrame 的子类,因此表格在文档中被作为框架处理。如果我们需要处理文档中的每一个框架,我们需要对它们进行区分,从而分别处理:

  1. QTextFrame::iterator it;
  2. for (it = frame->begin(); !(it.atEnd()); ++it) {
  3. QTextFrame *childFrame = it.currentFrame();
  4. QTextBlock childBlock = it.currentBlock();
  5. if (childFrame) {
  6. QTextTable *childTable = qobject_cast<QTextTable*>(childFrame);
  7. if (childTable)
  8. processTable(frameElement, childTable);
  9. else
  10. processFrame(frameElement, childFrame);
  11. } else if (childBlock.isValid())
  12. processBlock(frameElement, childBlock);
  13. }

对于表格中已存在的单元,可以通过行和列进行遍历:

  1. for (int row = 0; row < table->rows(); ++row) {
  2. for (int column = 0; column < table->columns(); ++column) {
  3. QTextTableCell tableCell = table->cellAt(row, column);
  4. processTableCell(tableCell);
  5. }
  6. }

列表

列表由 QTextList 类提供。

列表是一系列按照一般方法格式化的文本块,同时有一个列表的修饰,例如一个点和列表项。列表可以嵌套。如果列表格式指定了非零缩进,列表就会有一定的缩进。

我们可以通过列表索引指定每一个列表项:

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

QTextList 实际上是 QTextBlockGroup 的子类,因此,它并不是将列表项当做子元素,而是提供另外的方法管理它们。这意味着,当我们遍历文档的所有文本块时,有可能它是一个列表中的一项。我们应当使用下面的代码进行区分:

  1. QTextFrame::iterator it;
  2. for (it = frame->begin(); !(it.atEnd()); ++it) {
  3. QTextBlock block = it.currentBlock();
  4. if (block.isValid()) {
  5. QTextList *list = block.textList();
  6. if (list) {
  7. int index = list->itemNumber(block);
  8. processListItem(list, index);
  9. }
  10. }
  11. }

图像

在 QTextDocument 中,图像当做一个文本片段,这个文本通过 Qt 资源机制指向图片的外部链接。图像通过光标接口创建,通过改变图像文本片段的样式进行修改:

  1. if (fragment.isValid()) {
  2. QTextImageFormat newImageFormat = fragment.charFormat().toImageFormat();
  3. if (newImageFormat.isValid()) {
  4. newImageFormat.setName(":/images/newimage.png");
  5. QTextCursor helper = cursor;
  6. helper.setPosition(fragment.position());
  7. helper.setPosition(fragment.position() + fragment.length(), QTextCursor::KeepAnchor);
  8. helper.setCharFormat(newImageFormat);
  9. }
  10. }

表示图像的片段可以通过遍历包含图像的文本块的所有片段获得。

http://devbean.blog.51cto.com/448512/442632

http://devbean.blog.51cto.com/448512/447358

Qt 富文本处理(QTextDocument和QTextBlock和QTextFrame和QTextTable和QTextList和QTextDocument)的更多相关文章

  1. Qt富文本编辑器QTextDocument

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Qt富文本编辑器QTextDocument     本文地址:https://www.tech ...

  2. Qt 富文本处理

    富文本处理 所有的类围绕 QTextDocument 展开, 它保证了用户可以创建和修改 富文本块, 而无须定义中间语言.一个 QTextDocument 可以通过两个接口操作, 一个是用于编辑的 C ...

  3. PyQt(Python+Qt)学习随笔:富文本编辑器QTextEdit功能详解

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 一.概述 QTextEdit是一个高级的所见即所得的文档查看器和编辑器 ...

  4. Qt之QTableView显示富文本

    简述 对于QTableView中的显示,我们前面介绍过很多种,其中包括:文本.进度条.复选框等,今天我们介绍一下关于富文本的显示. 可能绝大多数小伙伴会通过QAbstractTableModel中的d ...

  5. 在线富文本编辑器FckEditor配置(.Net Framework 3.5)

    进入FCKeditor文件夹,编辑 fckconfig.js 文件.1.上传设置  .  var _FileBrowserLanguage         = 'php' ;         // a ...

  6. QTableView使用HTML显示富文本

    对于QTableView中的显示,我们前面介绍过很多种,其中包括:文本.进度条.复选框等,今天我们介绍一下关于富文本的显示. 可能绝大多数小伙伴会通过QAbstractTableModel中的data ...

  7. 富文本编辑器Simditor的简易使用

    最近打算自己做一个博客系统,并不打算使用帝国cms或者wordpress之类的做后台管理!自己处于学习阶段也就想把从前台到后台一起谢了.好了,废话不多说了,先来看看富文本编辑器SimDitor,这里是 ...

  8. 个人网站对xss跨站脚本攻击(重点是富文本编辑器情况)和sql注入攻击的防范

    昨天本博客受到了xss跨站脚本注入攻击,3分钟攻陷--其实攻击者进攻的手法很简单,没啥技术含量.只能感叹自己之前竟然完全没防范. 这是数据库里留下的一些记录.最后那人弄了一个无限循环弹出框的脚本,估计 ...

  9. 图解DevExpress RichEditControl富文本的使用,附源码及官方API

    9点半了,刚写到1.2.   该回家了,明天继续写完. 大家还需要什么操作,留言说一下,没有的我明天继续加. 好久没有玩DevExpress了,今天下载了一个玩玩,发现竟然更新到14.2.5了..我去 ...

随机推荐

  1. the convertion between string and BlobColumn

    It's hard to find some samples about the convertion between string and BlobColumn.AddBlobData. It's ...

  2. Hibernate之工具类HibernateUtil

    原创文章,转载请注明:Hibernate之工具类HibernateUtil  By Lucio.Yang 1.最简单的工具类,实现SessionFactory的单例共享,session的管理 pack ...

  3. shell 脚本中$$,$#,$?分别代表什么意思?

    $0 这个程式的执行名字$n 这个程式的第n个参数值,n=1..9$* 这个程式的所有参数,此选项参数可超过9个.$# 这个程式的参数个数$$ 这个程式的PID(脚本运行的当前进程ID号)$! 执行上 ...

  4. [LeetCode]题解(python):049-Group Anagrams

    题目来源: https://leetcode.com/problems/anagrams/ 题意分析: 给定一个字符串数组,将用相同字母(包括个数)组成的字符串放到一起.比如["eat&qu ...

  5. linux 内核分析之list_head

    转自:http://www.cnblogs.com/riky/archive/2006/12/28/606242.html 一.链表数据结构简介 链表是一种常用的组织有序数据的数据结构,它通过指针将一 ...

  6. Gsoap 使用心得 2

                                         Gsoap 返回图片byte的困惑 前些日子刚使用gsoap将二进制文件上传(服务期端使用c# wcf 编写),上传功能实现没 ...

  7. referer htttp headers 统计信息 防盗链

    HTTP headers是HTTP请求和相应的核心模块,它承载了关于客户端浏览器.请求页面.服务器等相关信息.Referer是HTTP头中的一个属性,告诉服务器我是从哪个页面链接过来的,所携带的信息用 ...

  8. [移动网关]2G环境下资源下载有一定概率失败,客户端日志显示收到403错误

    2G环境下资源下载有一定概率失败,客户端日志显示收到403错误 问题现象: 测试同学在使用联通号码在移动网络环境下,访问连接得到的response_code出现是403,导致资源读取失败表情显示异常. ...

  9. C/C++用strncpy()与strstr()分割与匹配查找字符串

    最近做题遇到分割与匹配字符串的题目(hdu5311),看来别人的代码,才知道有strncpy()和strstr()函数,于是搜集了一点资料,记录一下基本用法. 一.strncpy() char * s ...

  10. BZOJ 1699: [Usaco2007 Jan]Balanced Lineup排队

    1699: [Usaco2007 Jan]Balanced Lineup排队 Description 每天,农夫 John 的N(1 <= N <= 50,000)头牛总是按同一序列排队. ...