尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

TRichView跨平台富文本组件在Delphi与Lazarus中的安装实战

TRichView跨平台富文本组件在Delphi与Lazarus中的安装实战 简介富文本处理是桌面与跨平台软件开发中的高频需求如何在不同的开发环境和操作系统下保持编辑体验一致是许多工程师面临的挑战。TRichView作为一套商业级富文本组件采用自绘引擎与树形文档模型不依赖系统原生控件因此在Delphi的VCL、FireMonkey以及Lazarus的LCL中都能提供高度一致的功能。它支持从Delphi XE7到Delphi 12等多个版本同时覆盖Free Pascal生态解决传统TRichEdit在跨平台时不得不重写逻辑的痛点。通过组件包内的条件编译与分层设计开发者可以在不同IDE之间复用核心代码实现文档的RTF、HTML、纯文本读写以及Unicode持久化。这类组件广泛应用于OA公文编辑器、考试系统答题区、单据设计器等场景有效降低富文本功能的开发与维护成本。本文围绕TRichView在真实项目中的安装、配置与迁移经验详细梳理了在Delphi和Lazarus下从包管理到控件落地的完整流程为需要构建跨平台富文本应用的团队提供参考。 如果你经常和Delphi、Lazarus打交道一定对TRichView不陌生。这个商业级的富文本组件在RAD Studio和Free Pascal生态里几乎是“想做一个像样的文本编辑器、又不想自己从零造轮子”时的默认选项。最近我在整理一个发布包TRichView-23.1-XE7-D12 Lazarus FS.7z文件名看起来平平无奇但它同时覆盖了Delphi XE7、Delphi 12和Lazarus三个跨度近十年的开发环境。这篇文章我想从它聊起把我在真实项目里安装、配置、迁移和使用TRichView做富文本开发的经验完整梳理一遍尤其是那些容易被忽略的跨平台差异和坑点。1. 从文件名读出版本架构23.1、XE7、D12与Lazarus怎么选1.1 TRichView不是RichEdit的“高级换皮”很多刚接触TRichView的人会把它等同于Delphi自带的TRichEdit这个认知在实际开发里会踩大坑。TRichEdit是Win32系统RichEdit控件的封装Windows上能用但换到Linux、macOS就得重写一套逻辑而TRichView是一套完全自绘的富文本引擎它不依赖操作系统本身的文本编辑控件绘制、命中测试、打印、导出全部由组件自己的代码完成。正因如此它才能在Delphi的VCL、FireMonkey以及Lazarus的LCL里保持几乎一致的编辑体验。从数据结构上说TRichView内部把文档组织成段落、行、内联对象的树形模型。你在界面上看到的每一个文字、图片、表格、超链接最终都会被拆成一块块“内容块”存进这个模型里。这也是它相比TRichEdit最大的优势你可以把富文本内容当成结构化数据去处理而不是拿着一个RichEdit句柄去做Windows消息级操作。这个组件家族里最常用的是这几个类TRichView只读展示型富文本控件用于预览、打印、报告输出。TRichViewEdit可编辑型控件支持光标、选择、键盘输入、格式刷等交互。TRichViewPrint负责把TRichView里的文档输出到打印机或PDF。DBRichView与数据库字段绑定的版本方便做富文本内容持久化。一个典型的应用场景是做OA系统里的公文编辑器、仿真考试系统中的富文本答题区域或者单据设计器里的文本块排版。用TRichEdit做这些事会非常吃力因为它的段落样式、表格支持、图片混排能力都不够灵活。1.2 文件名里的三个环境标识到底是什么意思TRichView-23.1-XE7-D12 Lazarus FS.7z这个命名实际上是在告诉使用者这个发行包同时支持三套目标环境标识对应环境诞生年代典型使用者23.1组件版本号表示2023年的第1个发布线2023所有用户XE7Delphi XE7RAD Studio XE72014老项目维护者D12Delphi 12RAD Studio 12 Athens2023年底新项目、升级项目LazarusLazarus IDE Free Pascal编译器持续更新开源跨平台用户一个发布包同时标注XE7和D12对维护老项目的团队特别重要。很多企业里还跑着Delphi XE7时代的系统换IDE不是升级一个软件那么简单涉及第三方组件授权、老代码兼容性、团队学习成本。TRichView把新老版本放在同一个包内意味着你可以先在新环境里做验证再把组件逐步引入老项目而不是被迫“all in”一个大版本升级。Lazarus用户看到这个包名也应该心里有数它说明组件维护方一直在跟进LCL的变动。Lazarus的更新节奏和Delphi完全不一样Free Pascal编译器的版本、LCL控件库的API都在持续演进如果组件发布方不紧跟很容易出现源码包在Lazarus里编译不过的情况。所以看到Lazarus出现在包名里基本可以放心它在当前主流Lazarus版本上能正常编译。1.3 FS.7z只是分发外壳别在没必要的地方纠结FS具体代表什么不同发布渠道有自己的习惯常见说法是FreeSource、FileShare或者某个版本分支的缩写。7z则是压缩格式。我个人的建议是不要去猜后缀含义解压后先看包内目录结构真正有价值的信息都写在里面的README、版本说明和demo工程里。解压之后你通常会看到这样的目录组织按IDE版本区分的源码目录例如Delphi12、XE7、Lazarus。运行时包和设计时包的工程文件Delphi下是.dpkLazarus下是.lpk。大量示例工程覆盖文本编辑、表格、图片、打印、拼写检查等场景。文档和升级说明。我见过不少人拿到包就开始找“安装程序.exe”其实这类组件很少做一键式安装标准做法是通过IDE的包管理器手动编译安装。理解这一点后面就顺了。2. 一套源码跨三个IDE的底层策略VCL、LCL与条件编译的真实分工2.1 VCL和LCL是“看起来一样”的两套底层Delphi原生使用VCL控件库Lazarus使用的是LCL。LCL在设计理念上高度模仿VCL很多类名极其相似比如TForm、TButton、TMemo在两侧都有。但LCL的实现底层完全不同它在Windows上可能调用WinAPI在Linux上调用GTK或Qt在macOS上调用Cocoa。同样的TNButton底层可能是三个不同GUI框架里的三种按钮。TRichView想要同时跑在这两套体系上核心策略是把“富文本引擎”和“UI宿主”分离。文字的存储、段落解析、格式计算、光标模型这些纯逻辑部分尽量不依赖具体GUI框架而控件的窗口句柄、消息循环、输入法、焦点处理这些部分再做VCL版和LCL版的适配层。所以你在查看组件源码时会发现大量条件编译指令比如{$IFDEF LCL} // LCL下的控件创建逻辑 {$ELSE} // VCL下的控件创建逻辑 {$ENDIF}这个结构决定了使用TRichView时的一个行为准则尽量通过公开的API和属性操作内容不要直接访问控件的内部Canvas或底层的窗口句柄做自定义绘制。因为内部实现可能随版本和平台切换你写在VCL下能跑的绘制代码到LCL下可能连对象类型都不一样。2.2 条件编译与单元分层源码不乱靠的是隔离从工程结构上看TRichView通常把代码分成三层。第一层是核心引擎单元负责文档模型、格式化逻辑、导入导出过滤器。这些单元与UI无关可以在任何目标平台上编译。第二层是平台适配单元负责把引擎接到具体的控件库上。VCL版有一组单元LCL版有另一组单元它们对上层暴露接近一致的接口。第三层是设计时包负责让组件出现在IDE的组件面板里以及提供属性编辑器、组件编辑器这些开发期能力。这一层只在IDE里编译安装时需要。理解了这层隔离你就明白为什么一个包能同时支持XE7、D12和Lazarus。组件维护方只需要维护一份核心引擎代码不同IDE版本之间的差异被约束在适配层和设计时包内。这也提醒我们拿到源码后不要贸然去改核心引擎里的文件一旦改了后续升级组件时会面临大量冲突。真正需要定制功能时优先考虑继承组件类或者使用事件钩子实在要改源码至少做一份完整的diff记录。2.3 从XE7到D12Delphi自身的变化比想象中大有人会问Delphi XE7和Delphi 12不都是Delphi吗一个源码包为什么还要单独适配这里面的坑其实不少。从XE7到D12编译器版本变了RTL和FCL库的API有调整第三方组件依赖的底层单元名也换过。尤其在高DPI显示方面Delphi 10之后的版本对DPI感知重新做了处理很多老组件在高分屏下会出现字体模糊、图标过小的问题。TRichView在23.1版本里要同时兼容两个时代的IDE就必须在代码里检查编译器版本让高DPI支持只在新IDE下生效在老IDE下退回传统行为。还有一个容易被忽略的问题包的单元名冲突。有些组件在XE7和D12下编译出来的DCU格式不兼容如果你不小心把两个版本的输出目录混在一起会出现“File not found”或者“Unit was compiled with a different version of System.Types”这类错误。所以安装时最好把不同IDE版本的输出目录严格分开Library Path里不要同时挂多个版本的源码目录。3. 三端安装不是三倍工作量从解压到拖控件上窗体的完整路径3.1 动手前的准备路径、IDE状态、源码完整性安装之前我建议你先花五分钟确认三件事。第一解压路径不要包含中文、空格和括号。Delphi的老版本编译器对含空格的路径处理并不完美Lazarus在某些Linux环境下对中文路径也容易出幺蛾子。我习惯把组件放在一个纯英文路径下比如D:\Components\TRichView23。第二确认你的IDE版本和更新版本号。Delphi 12可能有不同的release更新Lazarus也有不同的发行版组件包内如果有多平台适配分支最好查看readme里关于版本兼容的说明。第三确认压缩包完整。7z文件如果从网盘下载经常因为传输中断导致解压到一半报CRC错误。解压工具一般会提示但有些人会点“忽略”继续最后编译时出一堆莫名其妙的问题。宁可重新下载一次也不要带病安装。3.2 Delphi 12下的安装步骤先运行时包再设计时包Delphi类组件的安装逻辑是分两步走的编译运行时包然后安装设计时包。具体操作是这样在源码目录中找到对应Delphi 12的子目录里面通常有一个.groupproj组工程文件或者多个.dpk包文件。用Delphi 12打开这个组工程。找到名字里带Run或Runtime的包右键选择Build。构建成功后找到名字里带Design或DT的包右键选择Install。如果Install选项是灰色的说明设计时包没有正确依赖运行时包先检查包的Requires列表。安装完成后打开IDE的Component菜单如果看到RichView相关组件出现在控件面板上就说明设计时包安装成功。这里有个操作顺序的原因值得说清楚运行时包是纯功能代码不依赖IDE环境设计时包负责把控件注册到IDE里它要引用运行时包。如果先安装设计时包编译器不知道运行时包的路径会直接报错。先Build再Install能让设计时包在编译时正确找到运行时包生成的DCU或DCP文件。3.3 Delphi XE7下的安装差异老版本IDE的脾气要顺着XE7下的安装步骤和D12基本一致但有三个细节必须注意。XE7最高只能识别旧格式的组件包文件如果源码目录里同时提供了新版和新格式的工程文件你要确认打开的是XE7版本目录里的那份而不是D12目录里向下兼容过来的。XE7在Windows 10/11上运行时偶尔会因为有边框阴影导致IDE组件面板显示异常。这不是组件的问题是IDE版本太老。如果遇到可以在Windows的兼容性设置里把“替代高DPI缩放行为”改成“应用程序”大部分情况下能解决。XE7环境下千万不要去引用D12编译出来的DCU或BPL跨版本库文件混用是最容易引发编译崩溃的做法。每个版本建立独立的输出目录各编各的最安全。3.4 Lazarus的安装流程lpk与重建IDELazarus的安装逻辑和Delphi不太一样。它不是“编译后再安装”而是通过包管理器直接加载.lpk文件然后选择Install让Lazarus重新编译自己把组件嵌进IDE里。流程如下打开Lazarus的Package菜单选Open Package File。找到Lazarus对应目录下的.lpk文件并打开。在弹出的Package窗口中先点Compile确认能编译通过。再点InstallLazarus会询问是否需要重建IDE选是。等待IDE自身重新编译完成后自动重启。需要注意Lazarus的包依赖关系比Delphi要严格。如果TRichView依赖了其他第三方LCL组件你必须先把那些依赖包在Lazarus里安装好再安装TRichView的主包否则编译会卡在找不到单元这一步。另外Lazarus在不同操作系统下的WidgetSet不同Windows下默认是Win32/Win64Linux下可能是GTK2或Qt5macOS下是Cocoa。同一个lpk包在不同的WidgetSet下的编译结果不能复用切WidgetSet后需要重新编译一次。3.5 用“新建一个demo工程”来验证安装是否真的成功安装完组件的第一个动作应该是新建一个空白工程拖一个TRichViewEdit到窗体上然后在FormShow里写一行最简单的赋值代码让它显示一段文字。procedure TForm1.FormShow(Sender: TObject); begin RichViewEdit1.AddText(Hello TRichView); end;编译、运行如果界面上能看到文字并可以编辑说明组件和IDE的对接没问题。我见过不少人在这一步就直接进入业务开发结果写了几天后才发现某个按钮功能异常最后追溯到组件安装不完整浪费大量时间。基础验证只要五分钟这五分钟值得花。4. 核心对象与高频API富文本的读写、图片与Unicode持久化4.1 先认识三个核心对象就知道典型项目要从哪里下手在TRichView家族里绝大多数业务场景都围绕三个对象展开。TRichViewEdit是最常用的编辑控件。它天然支持键盘操作、光标移动、选区高亮各种格式操作比如加粗、斜体、字号调整都有对应的简单API或默认的键盘行为。如果你的需求是让用户写一段“带格式的文本”直接从它开始就行不需要自己搭编辑框架。TRichView是只读控件。它和TRichViewEdit共用同一套文档模型所以你可以把一个编辑器的内容“塞”进一个只读控件里做预览或者反过来。设计器类项目里经常用TRichView做输出预览把TRichViewEdit当输入区域两者配合很顺手。TRichViewPrint负责文档输出。它把TRichView里的文档内容重新排版到打印画布上支持分页、页眉页脚、缩放。这里要注意打印的排版效果和屏幕显示不能画等号字体度量、页面宽度、DPI换算都可能让内容“看起来不同”本章最后我会专门讲这个坑。三个对象的取舍并不复杂编辑用Edit展示用View输出用Print。老项目里最常见的错误是拿一个TRichViewEdit去当展示控件用结果用户误点到文本还顺手改了几个字造成数据错误。4.2 加载和保存RTF、HTML、纯文本的读写姿势TRichView能处理的文档格式相当多RTF、HTML、纯文本是日常开发里用得最多的三种。如果把文本内容保存到文件直接调用RichViewEdit1.SaveRTF(output.rtf); RichViewEdit1.SaveHTML(output.html); RichViewEdit1.SaveText(output.txt);加载则是对应的一组方法RichViewEdit1.LoadRTF(input.rtf); RichViewEdit1.LoadHTML(input.html); RichViewEdit1.LoadText(input.txt);这里有一个很关键的点LoadText方法默认使用什么字符编码。Delphi XE7时代很多代码还在和AnsiString混着用如果一个txt文件是UTF-8编码直接LoadText进来可能中文全乱。TRichView的LoadText通常会提供编码参数或自动检测能力但自动检测并不总是可靠尤其在混合内容场景下。我的做法是凡是涉及文本文件导入导出的功能先明确文件的编码方式在调用LoadText或SaveText时显式指定编码不要依赖自动检测。比如UTF-8文件就明确指定UTF-8GBK文件就指定GBK。这个习惯能帮你避免大批量的乱码投诉。RTF和HTML的读写比文本文件复杂得多。RTF里可能包含图片和复杂的表格结构HTML则可能包含CSS样式和JavaScript。TRichView有自己的导入导出过滤器但它做不到在HTML层面百分百还原原因是HTML的盒模型和TRichView的段落模型并不完全对等。如果业务需要“富文本编辑器里粘贴网页内容保存后格式不丢”一定要先做测试贴一段混合了表格、无序列表、行内样式、图片的HTML看保存再加载后有多少东西丢了。4.3 Unicode、emoji与数据库写入把“incorrect string value”彻底说清楚富文本里出现emoji已经是常态表情符号、特殊符号、生僻字都会被用户直接粘贴进来。但在实际项目里很多人第一次做富文本持久化就会遇到这个报错Incorrect string value: \xF0\x9F\x90\xBB\xE7\x89... for column detail at row 1这不是TRichView的锅而是数据库字符集设置的问题。以MySQL为例utf8字符集最多支持3个字节的UTF-8编码而emoji普遍是4字节UTF-8序列比如某个动物表情的UTF-8编码就是\xF0\x9F\x90\xBB开头。你把富文本内容拼进SQL语句插入数据库时如果数据表字段是utf8而不是utf8mb4MySQL在解析字符串时会直接拒绝写库抛出错误。解决办法分三层第一层数据库连接字符集要指定为utf8mb4。连接MySQL后执行SET NAMES utf8mb4;或者在连接字符串里带上characterSetutf8mb4之类的参数。第二层表结构字段要使用utf8mb4ALTER TABLE your_table MODIFY COLUMN detail LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;第三层应用代码里写入数据库前把内容处理成UTF-8字节流并确保数据库驱动没有再做一次错误转码。Delphi和Lazarus在这件事上的处理方式不一样。Delphi从XE2开始默认字符串就是UnicodeString实际编码是UTF-16你在内存里看到的是两字节一单元而Lazarus的字符串默认是UTF-8编码至少在新版Free Pascal下如此。这意味着同一个富文本内容在Delphi里取出字符串后若要传给MySQL你需要在某一步完成UTF-16到UTF-8的转换在Lazarus里字符串往往已经是UTF-8直接传给数据库连接时会顺畅很多。TRichView的文本导出方法通常会提供一个参数指定目标编码或者直接返回UnicodeString。你在做数据库写入时最稳妥的路径是从控件拿到UnicodeString在应用层转成UTF-8再交给数据库并且数据库本身允许完整Unicode。这样无论内容里是中文、日文还是emoji都可以安全落库。4.4 图片与表格富文本内容完整性的两个判断点如果用户要在富文本里插图TRichView支持多种方式最常见的是AddImage或者通过剪贴板直接粘贴图片。图片数据默认以流的形式存在文档内部也可以按引用方式保存只记录图片文件路径。这里有两个容易踩的细节。第一“保存的doc里带图”不等于“图片内容真的存进数据库了”。如果你的业务将富文本保存为HTML字符串然后再写入数据库务必确认HTML里图片是转成了base64内嵌还是外链引用的路径。外链路径在另一台机器上打开时大概率显示空白因为图片不存在。第二表格是富文本里结构最复杂的部分TRichView支持表格嵌套、单元格合并、背景色等。保存成RTF时这部分信息可以完整保留保存成HTML时就会出现兼容性问题因为HTML表格的border和padding模型与RTF的表模型不一致。如果你的业务内容是强表格结构建议存储格式选RTF或TRichView自己的格式不要为了“方便Web展示”一律存HTML丢失了再后悔就晚了。5. 部署到真项目之前先看这五个踩坑现场5.1 行高和字体渲染同一份文档在Windows和Linux上“看着不一样”跨平台富文本开发里最让人头疼的不是代码逻辑而是字体渲染差异。Windows上微软雅黑渲染出来的行高和Linux上某个开源字体渲染出来的行高可能差出好几像素。TRichView虽然能跨平台绘制同一套文档模型但它无法改变操作系统底层的字体度量。实际表现就是同一份RTF文档在Windows下打开行距紧凑在Linux下打开可能每行都被拉高整个页面多了一页。我的解决方案是在应用启动时根据操作系统做一次字体映射方案配置。比如Windows下默认用微软雅黑Linux下映射到Noto Sans CJKmacOS下映射到PingFang SC。尽量明确指定字体族而不是放任系统去猜。TRichView允许在文档风格里统一设置字体先把全局字体定死再让用户基于这个基础调局部格式能显著减少跨平台行高问题。5.2 打印和PDF导出屏幕预览不是“所见即所得”的真相很多“所见即所得”类工具宣传得很好但真正做产品的人都清楚屏幕显示和打印输出不存在绝对一致。TRichViewPrint在打印时会把文档重新排版到目标页面上页面宽度和屏幕宽度不一样换行位置就必然变化。如果你发现打印出来的内容比屏幕预览多了几行或者某一行文字被截断先不要怀疑打印Bug去看看打印机的纸张尺寸和页边距设置再检查TRichViewPrint的缩放比例。TRichView里的打印缩放通常以100%表示“屏幕像素直接映射到设备像素”但打印机的DPI一般是300甚至更高你需要决定是按实际尺寸打印还是按页面宽度自适应。我习惯的做法是在界面上提供“打印预览”功能调用TRichViewPrint把内容渲染到一个画布上让用户先看排版结果而不是直接送打印机。用户看到预览效果后再点打印误打印率会低很多。5.3 释放对象时的访问冲突多数字段是“重复释放”不是“内存泄漏”用TRichView做动态文档生成时很多新手会写类似这样的代码var aView: TRichView; begin aView : TRichView.Create(nil); try aView.LoadRTF(a.rtf); // 处理逻辑 finally aView.Free; end; end;代码看起来没问题但如果TRichView内部某些对象注册到了全局管理器里Free的时候会触发二次释放就可能出现Access Violation。尤其是文档里包含图片、表格时内部子对象的生命周期依赖关系比普通控件复杂。我的排查经验是遇到释放阶段的AV先不要急着改内存管理策略第一步是打开FastMM或类似内存管理工具的完整报告看AV发生时的调用栈。如果问题出在“尝试释放一个已经被释放的对象”那大概率是你手动Free了组件持有的某个内部对象或者在不同窗体之间共享了同一个TRichView实例没做所有权转移。TRichView文档自己管理的子对象应该通过Clear或Reset方法统一清理不要靠手动遍历释放。5.4 DFM与LFM的鸿沟跨IDE打开窗体文件要留个心眼Delphi的窗体文件扩展名是.dfmLazarus的窗体文件是.lfm。两者看起来都是文本格式但属性体系和事件绑定方式有差异。TRichView有许多自定义属性在Delphi里保存为DFM属性在Lazarus里可能语义不完全一致。如果你维护的项目同时存在Delphi版和Lazarus版并且共用同一套窗体文件最好用组件提供的文本传输或专门的窗体转换工具先转出原格式再在目标IDE里打开。直接手动改DFM扩展名去打开几乎必然遇到属性不识别的问题。Lazarus打开DFM文件时通常会自动跳出一个对话框询问是否转换为LFM。有些组件属性名和Delphi完全一致转换后能直接用有些属性带枚举值Lazarus不支持相同的枚举名称这时你需要在LFM里手动把属性值改成Lazarus能识别的写法。我的建议是这种跨IDE转换只做一次转换完成后以LFM为主开发过程中不要来回用DFM覆盖LFM否则会出现大量重复劳动。5.5 版本升级后的行为变化23.1带来的不全是新功能组件从旧版本升级到23.1后有些老项目的表现会和之前不同。常见的变化集中在两个方面一是高DPI支持。新版组件在Delphi 10.3以上的环境下默认开启每英寸点数自适应控件里的图标和字体反而可能比老版本看起来小或大。你需要检查工程里的DPI设置确保和高DPI感知选项一致。二是Unicode处理。旧版本可能在导出HTML时把中文转成实体编码新版本直接输出原始字符。这不是Bug是行为调整。如果你的下游系统对HTML实体有依赖要注意升级后的输出内容变了。每次升级组件在工作开始之前先通读一次包内的更新说明文件把涉及行为变化的条目列成清单逐个排查对你的项目是否有影响。不要等测试报告出来再往回翻那会浪费整个迭代周期的测试时间。最后再说说我个人的习惯拿到任何新版本第一件事是把包内自带的demo工程全部编译一遍每个demo看一遍确认没有奇怪的编译错误或运行崩溃再动手接业务代码。TRichView的demo覆盖率很高文本编辑、图片、表格、打印、数据库绑定都有对应示例。这些demo不光是“能用”的证明更是最好的入门文档。很多看起来很难实现的效果比如审批流程里的会签意见栏、考试系统里的混合排版答题卡都能在demo里找到相近的起点改改就能用比从空窗体开始写要快得多。本文还有配套的精品资源点击获取
返回列表