人物采访 / T SALON

编辑内容

李泽磊谈阅读器排版引擎:跨端重构、阅读体验与团队成长

回顾 T Chat 第 10 期两段录播,从百度小说阅读器的 C++ 排版引擎、小样与大样、粗排与细排、中文排版细节,谈到工程师成长、技术收益、团队协作和职业选择。

T Chat 第 10 期李泽磊分享百度阅读器技术与成长经历的封面
T Chat 第 10 期李泽磊分享百度阅读器技术与成长经历的封面

本期视频发布于 2022 年。文中的技术状态、个人经历与观点均为当时情况。

阅读器看起来只是把文字放进屏幕,但文字怎样断行、分页,怎样选中,怎样在长时间阅读时保持流畅,都需要排版引擎做具体判断。李泽磊在 T Chat 第 10 期介绍百度小说阅读器的技术重构,再通过一对一访谈讨论从个人开发者走向团队负责人的变化。两段录播合计约 67 分钟。

排版引擎解决的,不只是文字能否显示

基线和标点,决定连续阅读的细节

分享从两组对比开始。同样的文字,如果没有按照合适的基线排列,视觉上会显得不协调;如果句号、逗号单独出现在下一行行首,读者的视线也容易被打断。平常将文字交给系统控件时,这些排版规则往往已经由系统处理,业务开发者不一定会注意到。

李泽磊将排版解释为在固定页面内,以合适的方式呈现内容。“合适”包含了很多具体策略。广义排版引擎可以包括解析、布局、渲染与显示的完整链路,而本次分享主要聚焦其中计算内容位置的排版模块。

开场时,他让听众读两遍同一段文字:第一种布局让标点或末尾单字的位置破坏阅读节奏,第二种按排版规则调整行尾与下一行。停下来比较两种读法,是为了说明一个细节:排版策略不是只给页面“加装饰”,它会影响读者是否能顺着句子继续读下去。

他也区分了流式排版和具有固定版面坐标的排版。前者会根据屏幕宽高重新组织文字,后者更强调保持既定版面。小说阅读器主要面对流式内容,但复杂文档中也可能需要结合两种思路。

为什么已有系统能力,还要重做阅读器

团队原先的 iOS 与 Android 阅读器采用不同实现。iOS 侧对 Core Text 的封装能力较有限,Android 侧沿用较旧的第三方框架版本。李泽磊指出的问题集中在三类:已有阅读功能不齐全,新的图文、表格或列表能力难以扩展;两端重复实现同一需求,维护成本高;排版效果和性能长期难以统一。

具体到旧实现,文本复制和划线选中等能力缺失;若以后接入 EPUB,图文混排、表格、列表也难在现有结构上扩展。同一个选中功能,要在两端分别设计、分别开发。李泽磊还提到当时打开旧阅读器的内存占用和卡顿问题;这些是他对旧项目的现场描述,不能套作所有阅读器的性能基准。

因此,新项目希望用 C++ 建立跨端共享的实现,掌握解析、排版与渲染的关键环节,并通过更好的阅读体验服务业务目标。这里的自研是针对团队既有问题作出的选择,并非认为系统排版能力本身不足以服务所有应用。

一套共享引擎怎样组织数据和工作

容器、引擎与基础能力分层

上层容器处理设置、滑动、书签、目录等阅读器功能。团队希望把适合共享的非系统逻辑也放进 C++,减少双端行为差异;中间是解析、布局和渲染等引擎模块;底层则封装线程、文件读写等公共能力。

与原先依赖相对封闭的排版过程相比,新的结构允许团队在多个环节进行干预。后续增加功能时,不必只在最终绘制结果上绕行,而是可以调整内容如何解析、怎样进入布局,再怎样输出给渲染层。

小样描述内容,大样描述排版结果

分享用“小样”和“大样”区分两类数据。不同文件格式首先解析为统一的小样数据;排版引擎遍历它、应用策略,生成面向渲染的大样数据。李泽磊用浏览器中的文档树和渲染树帮助理解二者关系。

小样侧主要组织内容与属性,大样侧包含实际位置等渲染信息。同一段文字也不必每个字符都生成一个独立对象,可以按照语言、属性等特征分组处理。属性存储的设计同样需要考虑使用方式与内存:没有必要的属性可以不保存,面向外部使用的数据则要兼顾清晰与易理解。

他拿一行文字解释“分组”:画面上有十八个汉字和三个标点,引擎按文字特征和标点边界分组,最后只有八个盒子,而不是为每个字符单独建一个同等级对象。这里的节省来自数据如何组织;小样还只保存实际存在的属性,大样则为了渲染与外部调用明确带上字号、颜色和坐标。

这种拆分让内容格式和排版策略不必紧密绑在一起。格式先转换成统一数据,之后再通过相同的引擎处理;需要新增效果时,也有对应的数据和策略入口。

为什么这次选择原生实现

面对“为什么不用 WebView”的问题,李泽磊从项目的解析、排版与绘制三个环节回答。团队希望针对书籍内容建立更集中的轻量实现;长篇内容还需要关注内存占用和大量页面的处理方式;引擎输出元素坐标后,原生层可以直接按结果绘制。

他把自己的实现类比为面向特定内容的轻量布局能力。这个解释对应当时项目对控制能力与性能的要求,也与团队已有技术积累有关,不能脱离这些条件推导成所有阅读器都应采用同一种技术路线。

从大文件分页到复杂版面

将分页与最终排版分开

旧阅读器打开大型图书较慢,一个重要原因是为了知道整本书能排多少页,需要提前生成大量完整排版结果。这既消耗计算时间,也会让尚未展示的页面长期占用内存。

团队把过程拆为粗排和细排。粗排只计算每页容纳哪些内容,生成记录起止位置的轻量分页信息;真正需要展示某一页时,再根据分页信息完成细排,生成完整渲染数据。那些影响最终视觉效果、却不影响分页边界的耗时策略,可以留到细排阶段执行。

这里最关键的判断是哪些工作只为分页服务,哪些工作只为最终视觉效果服务。先得到 PageInfo,阅读器就能回答“这本书有多少页”“当前页在哪里”;用户真正翻到某页时,再根据那一页的边界构建带坐标的大样。粗排不是把细排随意做得粗糙,而是明确推迟那些此刻不需要的计算。

李泽磊用一本约五千页的书说明内存成本:若为了算页数提前保存每页完整的大样,按团队当时估算,会占用约三四十兆内存;而仅记录每页起止节点的 PageInfo,结构浅得多。左右对齐等策略需要在最终展示时处理,却不一定影响一页容纳多少内容,因此可以留到细排。这些估算对应当时的实现和示例,不能直接套到其他引擎。

接口也相应拆为分页和针对某页构建详细结果两类,让调用方按需取得数据。

首字下沉、拼音与嵌套排版

基本流程是遍历小样节点,计算内容在页面中的位置,再写入大样。但首字下沉会占用后续若干行的一部分空间,排版器不能只按整行宽度一路向下计算。

首字下沉的演示里,一个放大的首字占去了接下来几行左侧的空间。引擎因此记录仍在占位的区域;排到这些行时,先重新算可用宽度,再决定其余文字能放在哪里。等排版跨过首字覆盖的范围,后续行又可使用完整宽度。这个小例子展示了为什么排版器不能只维护一个不断右移的光标。

演示里,他把一个汉字及其上方的拼音先作为整体盒子,再在盒子内部排放拼音和居中的汉字;最终绘制之前,才把相对坐标转换为页面坐标。公式中的分子、分母也可用类似的嵌套思路理解。它和首字下沉解决的问题不同:前者处理元素内部的相对位置,后者改变随后几行可用的排版区域。

完整的位置数据也支撑划线选中

大样数据的用途并不止于绘制。实现文本选中时,上层传入手势坐标,引擎需要判断它落在哪个段落、哪一行、哪个文字,并根据选区起止位置找出对应内容。

他在内核 Demo 中用命中测试展示这些位置:要选中标题里的两个字,先根据触点找到段落,再找到行与字,并结合选区两端的坐标确定范围。屏幕上看到的是一句连续的文字,引擎里却已经有每个字的位置和索引。这个对应关系让“画出字”和“用户点中了哪个字”能使用同一份排版结果。

阅读体验需要持续打磨

内容完整、滑动流畅与版面细节

李泽磊把阅读体验分成三个方面。首先是正确支持内容:如果书中的关键表达无法呈现,其他优化很难弥补。其次是连续使用时的流畅性,既包括快速滑动,也包括 CPU 和内存开销对长时间阅读的影响。最后才是更细的版面打磨。

他用“版面灰度”解释文字、标点与空白分布带来的整体观感。空白过于突出,会在连续阅读中形成视觉断点;排列过紧,又会产生拥挤感。分享通过行首和行内的标点挤压策略,说明如何调整某些全角标点周围的空间,让移动设备上有限的行宽得到更协调的利用。这只是团队所做细节优化中的两个例子。

字体度量与国际化,都是排版问题的一部分

最后的知识补充回到字体本身。字符前进宽度与可见字形宽度并不相同,还要考虑两侧留白;字形相对基线的上下范围,也参与排版计算。不同字母组合还可能需要字距调整,不能只把每个字符的固定宽度机械相加。

他把字形宽度、两侧留白和前进宽度分开解释。两个字号相同的汉字,即便按各自前进宽度依次摆放,可见笔画之间仍可能留有空间;英文中 VA 的字距调整又会让第二个字母向前靠近。基线之上的 ascent 与下方的 descent,则帮助引擎理解字形在竖直方向占据的位置。画一个矩形框并把字放进去,不足以完整描述这些度量。

最后,他以从右向左书写的阿拉伯文留下思考题:当阅读产品进入新的语言市场,原有排版假设需要重新检查。录播没有给出完整的双向文本实现方案。

从独立开发者到团队负责人

关注业务收益,也理解成员的技术诉求

第二段开始,李泽磊介绍了当时负责百度小说客户端团队的工作。阅读器排版部分逐渐稳定后,团队仍面对接手代码中的历史负担、模块耦合和架构调整。负责人除了参与技术,还需要更深入地考虑业务结果与成员成长。

他理解开发者希望参与有技术深度的项目,因为自己也经历过这一阶段。管理上的工作之一,是观察成员的兴趣和诉求,寻找能够与业务需求结合的技术项目,让团队目标与个人成长尽量相互支持。

成长表现为负责的问题范围不断扩大

他把成长描述为不断重新认识技术的过程。最初是实现界面、满足功能;之后开始关注代码质量,研究第三方库和系统 API 的设计;再往后,独立负责复杂模块,需要考虑接口、内部拆分和使用方式。

主持人顺着这个阶段划分追问:从独立写模块到带项目,转变到底在哪里?李泽磊说,前者可以把重点放在自己设计的接口和代码;后者要让几个人的工作在同一目标下连接起来。他也笑谈,写模块时容易认为自己的代码最好,隔一段时间回头看,才发现以前批评过的写法可能就是自己留下的。这种自我修正,是对“技术越来越强”的另一种解释。

负责多个项目时,问题还会扩大到哪些能力值得共享、哪些技术选择应保留项目差异。技术深度仍然重要,但“把自己这一块代码写好”已经不足以覆盖全部责任。

挑战不总出现,失败也能留下经验

被问到成长中最重要的几点时,李泽磊没有给出固定的三条公式。他更看重工作中的具体挑战:新技术领域会推动深度或广度,大型项目会训练协作与管理。日常工作不可能时时充满挑战,遇到机会时需要主动投入。

他也承认不是每次尝试都会成功。挫折可能发生在技术上,也可能发生在项目推进中;继续分析和解决问题,仍会留下下一次可以使用的经验。对谈中的积极态度,是面对困难继续行动,而非把每次挑战都描述成必然成功。

底层学习需要方向、实践和必要基础

对于排版、音视频等较底层的技术,他建议尽量结合真实使用场景学习。只看书或文章却长期不使用,容易遗忘,也不容易发现真正的工程问题。C++ 等基础能力能够帮助开发者进入这些跨平台领域,但语言本身并不等于领域知识。

李泽磊特别提到,自己学习排版时得到过领域专家的指导。排版规则、技巧和编码知识很多,有人帮助建立方向会降低摸索成本。如果暂时没有工作场景,也可以自己设计一个项目并完整实现,通过实际结果检验理解。

职业选择要看更长的时间范围

他的职业困惑更多来自选择:继续深入技术,还是承担管理;维持现有方向,还是探索跨平台。判断时,他会把时间拉长,思考未来一两年自己希望得到什么,以及兴趣真正在哪里,而不是只追随短期热度。

换工作也有需要计入的成本,例如进入新环境、重新建立信任和协作关系。薪酬与工作内容当然重要,但并不是全部变量。这是他个人的决策方式,访谈并未给出适用于所有人的统一职业答案。

技术演进怎样与团队结果连接

收益和适合,是选型的两个判断维度

李泽磊谈到,架构改进往往重要,却不像紧急缺陷那样立即阻断业务。因此团队提出技术项目时,需要说明实际收益。“降低耦合”“架构更清晰”描述了技术变化,但还应继续解释它们怎样减少维护成本、缩短开发时间或改善交付。

与此同时,技术方案要适合团队的技术栈、经验和项目特征。其他团队成功使用的跨平台方案,并不自动适合自己的场景。架构选择应当把引入、学习、维护和后续使用的成本一起纳入判断。

招聘既看平台能力,也看工程基础与表达

他把“iOS 开发工程师”拆成两个维度:在具体平台上有相应技术基础和深度;作为工程师,又具备计算机原理、算法、数据结构等通用能力。软实力则关注沟通是否清楚、逻辑是否连贯,因为合作需要把问题解释给别人。

不同阶段也对应不同责任:先能稳定完成岗位工作,再能独立承担模块,随后能够从技术与业务两侧推进项目。越往后,主动性、责任感和协同能力越难省略。录播中还出现当时的招聘交流,属于历史信息,不代表当前岗位开放情况。

探索可以不确定,但应说清楚为什么值得尝试

主持人借技术项目的绩效描述继续追问:写“降低耦合”或“架构更清晰”,究竟算什么结果?李泽磊认为这只是变化本身,还要说它如何让团队更少花时间维护、让新需求更容易交付。讨论并没有给出这个阅读器重构前后的可核实效率增幅,因此文章也不把主持人举出的假设数字写成项目成果。

现场追问:如果强调收益,那些不确定的探索该怎样做?李泽磊的回答是,探索可以开展,但要说明问题、方向和预期收益的大致范围。无法提前保证结果,与完全没有判断依据,是两种不同情况。

他以引入跨平台技术为例:如果为了很少的页面引入庞大工程和新的技术栈,最后使用范围又非常有限,可能并没有达到最初承诺的效率目标。探索后的实际使用与维护成本需要被回看,不能仅凭方案流行或演示成功认定收益已经兑现。

行业变化与后续方向

李泽磊先说了一个停车的日常例子:办公区车位有限,自己遇到同事的车时也会担心最后一个车位被占。他用它解释资源有限且有人愿意争取时为何会有竞争;绩效、晋升和奖金也会让行业里的竞争长期存在。主持人则把问题转向行业增速:过去业务高速增长时,低效工作也存在,只是更容易被增长掩盖;增长放缓后,团队更需要分清有效投入和无效消耗。这是两人在 2022 年环境中的观察,不是对整个行业的定论。

讨论随后回到移动端工程师的空间。如果只把能力理解为某套 UI 框架,视野容易变窄;把目光扩展到终端、系统和更底层能力,则可能连接到不同设备与业务。前半场的排版引擎,正是这种从业务需求向下深入的实例。

在当时的后续规划中,团队准备继续改进工程架构和性能,分别回应研发效率与阅读体验。个人方面,李泽磊希望进一步探索 EPUB 的复杂排版能力,但也说明业务当时尚无接入相关资源的迫切需求。技术兴趣与业务优先级之间,需要持续安排投入顺序。

查看本期原视频与分段信息:我在百度做阅读器 →