把 Word 的一页纸搬进浏览器:Canvas 渲染引擎的逐像素之路
“ 一次围绕 Word 原生排版、OOXML 规则、字体度量与 Canvas 绘制的高保真文档预览实践。

有些需求,乍一听只是“在线预览一个 Word 文档”。
真正做进去才知道,这句话背后藏着一座精密的城市。段落要知道自己的前后间距,表格要记得每一根网格线,目录的点线要一路铺到页码前,图片要服从环绕规则,页眉页脚要随节切换,字体还要像 Word 一样决定每一个字究竟占多宽。
如果差一点,用户不会说“渲染引擎的度量模型有待改善”。用户只会看到:这里换行了,那里溢出了,表格行高不对,页码没有对齐,第一页公司名称短了一截。
于是我们开始做一件看似朴素、却极其挑剔的事:把 Word 的一页纸,尽可能原样搬进浏览器。
为什么这件事一直难
办公软件里的 Word 文档,远不只是文本加图片。它是一套完整的排版语言。
市场上常见的方案大致有三类:一种把 Word 转成图片或 PDF,视觉接近,但内容失去生命,搜索、选择、跳转、结构化交互都变得困难;一种用普通 HTML 还原,轻盈灵活,却很容易在字体、表格、分页上与 Word 分道扬镳;还有一种依赖服务端转换,效果不错,却带来部署、并发、隐私、成本和跨平台一致性的压力。
真正适合线上产品的方案,必须同时满足四件事:在浏览器里运行,保持文档结构,接近 Word 原生版式,并且能够在复杂文件里保持响应速度。
这正是 Canvas 渲染路线的价值所在。Canvas 不再把排版完全交给浏览器的自然流式布局,而是由引擎先读懂 Word 文档,再用可控的度量、分页和绘制过程把页面画出来。浏览器负责呈现,排版逻辑回到文档规则本身。
从“看起来像”到“逐页对齐”
最早暴露问题的,并不是长篇大论,而是一张图。
Word 里原本是一条竖线,到了预览里却变成了粗柱。接着,页边的 L 型标记方向反了,目录点线无法对齐,表格内某些行的高度没有被应用,长文本在单元格里提前换行,页面底部甚至出现了内容溢出。
这些问题没有一个适合用“针对这个文档补一刀”的方式解决。因为今天可以对一个文件生效,明天就会在另一个客户文档里留下新的裂缝。
我们选择回到 Word 的规则:读取 OOXML 里的真实属性,解析默认样式、段落属性、表格网格、字体声明、节信息、分页标记、字段结果,再用 Word 的实际渲染效果逐页对照。

图中最容易被忽略的地方,往往最考验排版引擎。比如封面底部的公司名称,并不是“居中放一行文字”那么简单。它受到字体、字符间距、段落宽度、图片位置和页面边界共同影响。任何一个环节偏移,最后都会变成肉眼可见的不一致。
目录不是文本,是一条精密的路
目录页看起来简单:标题、章节名、点线、页码。
但 Word 里的目录常常来自复杂字段。它包含内部跳转、缓存的页码、右对齐制表位、点状前导符、不同层级的缩进和样式继承。若只是把文字拼出来,点线会断,页码会飘,点击跳转也会消失。
我们保留 Word 已经写入文档的字段结果,同时恢复目录项的链接能力。页面上的目录既像打印稿一样黑白整齐,又能像网页一样点击跳转。

这类能力对手册、规范、标书、制度文件尤其重要。用户看到目录时,期待的不是“差不多的文本列表”,而是一张可以进入全文的地图。
表格,是最容易露出破绽的地方
Word 表格并不等同于网页表格。
它有固定布局,也有自动布局;有显式列宽,也有网格跨度;行高可能是最小值,也可能是精确值;单元格里还有内边距、垂直对齐、段落间距、换行规则、边框合并和底纹。更麻烦的是,表格经常跨页,表头需要重复,某些行不能拆,某些内容又必须在下一页继续。
这也是我们投入最多的部分之一。引擎会解析 tblGrid、tcW、gridSpan、rowspan、tblLayout、行高规则和单元格段落属性,让表格的每一行不是“看起来平均分”,而是来自文档本身。

长文本表格更能放大差异。字体度量稍微偏宽,整段文字就会提前换行;稍微偏窄,页面又会空出一截。我们把字体选择、字号换算、字符占位、东亚断行、英文长串处理放在同一套度量通道中,避免段落和表格各自为政。

字体,是像素一致的钥匙
很多 Word 预览的偏差,表面看是分页错了,深处其实是字体错了。
同样的字号,在不同字体里宽度不同;同样一个中文逗号,在不同 shaping 引擎里也可能占位不同。文档一旦混入中文、英文、数字、URL、代码片段和标点,差距会一路传导:一行变两行,一页变两页,表格高度随之改变,后面的内容全部偏移。
因此我们没有把字体打包进产物。更合理的方式,是尊重客户机已有的字体环境:通过本地字体授权访问读取可用字体,建立字体网关,加载文档时显示进度,让渲染引擎在度量阶段使用与 Word 更接近的字体族、字重和样式。
这样做有两个好处。第一,版权清晰,组件不携带客户不需要的字体文件;第二,版式更接近真实使用环境。企业内网、政务系统、学校平台、设计单位各自有自己的字体资产,预览组件应该适应它们,而不是用一套固定字体覆盖所有文档。
Canvas 不是一块画布,而是一条流水线
为了让文档在浏览器里既精确又流畅,渲染过程被拆成了几段。
DOCX 文件先在 Worker 中解包,读取 document.xml、styles.xml、numbering.xml、settings.xml、关系文件、页眉页脚、脚注尾注、媒体资源和字段信息。主线程拿到轻量化的文档快照后,再分批构建页面,等待图片与字体加载,执行分页测量,最后进入 Canvas 绘制。
这个过程不是为了炫技,而是为了让大文档不阻塞界面。用户上传上百页手册时,页面应当有进度、有响应、有可预期的等待,而不是把浏览器拖进漫长的沉默。
我们真正解决了哪些问题
这套组件围绕 Word 原生版式补齐了多类核心能力:
-
完整解析文档默认样式、段落样式、字符样式与样式继承。 -
应用段前段后、行距、首行缩进、悬挂缩进、制表位、项目符号与编号后缀。 -
支持节、页眉页脚、首页不同、奇偶页规则、页码字段与跨页分页。 -
支持 Word 保存的分页位置、显式分页、段落分页控制、孤行控制和动态溢出分页。 -
支持目录字段、页码缓存、点状前导符、内部锚点与点击跳转。 -
支持固定表格、跨列跨行、行高规则、单元格边距、边框、底纹、跨页表头重复。 -
支持图片、DrawingML、VML、组合形状、常见图表、SmartArt、公式与墨迹的原生预览路径。 -
支持中文排版规则,包括文档网格、禁则、标点溢出、自动中西文间距和东亚字体回退。 -
支持本地字体网关,按客户机授权字体进行加载、度量和渲染,不在产物中内置字体文件。 -
支持逐页截图、差异图和像素级回归比对,让每一次优化都能被量化。

它能支撑什么
对产品来说,高保真 Word 预览不是一个孤立功能,而是一层基础设施。
在政务、金融、教育、医疗、制造和法律场景里,大量流程仍然围绕 Word 文档展开。制度文件要在线阅读,合同要留痕审阅,标书要多人校对,接口文档要精确分享,审批附件要在移动端打开。用户不只是要“看见文字”,而是要相信它和原文档一致。
当渲染引擎能够保留页码、目录、表格、图片、段落和字体位置,很多过去只能下载后打开的流程,就可以留在浏览器里完成。预览、检索、定位、批注、比对、权限控制、审计留痕,都有了同一张页面作为基础。
最后
做 Word 渲染,最迷人的地方在于它不允许偷懒。
一根线画粗了,说明形状解析错了;一个句号单独换行,说明字体度量错了;一行表格高度不对,说明行高规则没有被尊重;目录页码没有贴右边,说明制表位和字段结果还没有真正回到 Word 的世界里。
这些细节细小,却并不琐碎。它们共同组成用户对文档的信任。
我们希望这套 Canvas 渲染组件最终成为一种安静而可靠的能力:当一份 Word 文档进入浏览器,它不再像一份被重新解释过的网页,而像它本来就在那里。纸张、文字、表格、图片、页码和空白,都各安其位。
夜雨聆风