mermaid-ascii 的 CJK 对齐秘密:全角字符双格占位实现剖析
2026/9/20 14:06:15 网站建设 项目流程

mermaid-ascii 的 CJK 对齐秘密:全角字符双格占位实现剖析

【免费下载链接】mermaid-asciiRender Mermaid graphs inside your terminal项目地址: https://gitcode.com/GitHub_Trending/me/mermaid-ascii

mermaid-ascii 是一款在终端里渲染 Mermaid 图(流程图、时序图、ER 图)的开源命令行工具。当你把节点名写成"顾客"、"数据库"这类中文字符时,很多渲染器会把图框撑得歪歪扭扭——而它靠全角字符双格占位这一关键设计让 CJK 字符在终端里严丝合缝地对齐。这篇文章带你剖析这套机制背后的原理与实现细节。

为什么终端里的 CJK 对齐这么难?

在终端的等宽网格中,每个"列"的宽度是固定的:

  • 半角字符(英文、数字、ASCII 符号):占1 格
  • 全角字符(中日韩汉字、全角标点):占2 格

如果渲染器按"字符个数"而不是"显示宽度"来计算位置,一个 4 个汉字的节点名就会被当成 4 格而非 8 格处理,方框、箭头、生命线随之全部错位。这是所有终端绘图工具在支持中文时的第一道坎。

核心原理:runewidth 测量显示宽度

mermaid-ascii 的解法是引入mattn/go-runewidth库——它是 Go 生态中计算 Unicode 显示宽度的事实标准。项目几乎在所有绘制路径上都用它来度量文本:

  • 流程图绘制:pkg/graph/draw.go
  • 节点标签排版:pkg/graph/label.go
  • 时序图单元格拆分:pkg/sequence/renderer.go
  • ER 图表格列宽:pkg/er/renderer.go

以流程图绘制文字为例,drawText的逻辑只有两步:

  1. 先用runewidth.StringWidth(text)算出整段文字的视觉宽度,据此扩展画布;
  2. 再逐个 rune 遍历,宽字符(如汉字)写入单元格后,把右边相邻的格子标记为续格,再前进runeWidth列。

这样"数据库"三个字在内部画布中恰好占据 6 列,方框边界和箭头端点自然落在正确的位置上。

时序图则更进一步:pkg/sequence/renderer.go 中的runeCellWidth把每个字符拆成"单元格"数组,宽度为 2 的字符后面追加一个continuationCell(续格),保证任何写入操作都不会"插队"到汉字中间,破坏显示。

边界情况:零宽组合标记怎么办?

CJK 文本常伴随零宽组合字符(如音调符号),它们显示宽度为 0。cmd/testdata/sequence/leading_combining_marks.txt 这类测试用例就专门验证了这个场景。

实现上的处理很巧妙(见 pkg/sequence/renderer.go 的putTextBefore):

  • 遇到宽度为 0 的字符时,附着到前一个有宽字符的单元格上,而不是占格;
  • 如果前面还没有基础字符,就先"挂起",等下一个有效字符出现时再合并写入。

这避免了组合标记错误地附着到填充空格上导致的字符错乱。

隐藏陷阱:East Asian Width 模式

还有一个容易被忽略的坑:制表符本身(│、─)的宽度会随终端 locale 变化。在东亚宽字符模式下,这些字符可能被测量为 2 格宽,导致同一份输出在不同终端上表现不一致。

mermaid-ascii 从两个层面化解了这个问题:

  • 字符集层面:在 pkg/sequence/charset.go 中,Circle特意选用字母o而非 Unicode 圆圈,源码注释明确写道——后者是 East-Asian-ambiguous 宽度,会在支持 CJK 的终端上破坏列对齐。这个细节体现了作者对宽度模型的深刻理解;
  • 测试层面:pkg/sequence/renderer_test.go 专门构造了EastAsianWidth = true的运行环境,对 east_asian_participants.txt 等用例做回归验证,确保宽字符模式下参与者方框之间依然留白正确。

实际效果:一张全 CJK 的时序图

测试数据 cmd/testdata/sequence/cjk_feature_matrix.txt 是一份"压力测试":日语 box 标题、中日混合的参与者(顧客 / 服务 / 監査 / 数据库)、自消息、Note、alt/loop 嵌套块全部用 CJK 命名。得益于双格占位机制,所有方框宽度、箭头长度和嵌套边框的拐角都能精确对齐,即使中英日韩混排也不散架。

小结

mermaid-ascii 让终端 Mermaid 渲染稳稳支持中文,靠的不是什么魔法,而是三条工程原则:

  1. 一切排版以显示宽度为准——runewidth贯穿流程图、时序图、ER 图所有绘制路径;
  2. 全角字符双格占位 + 续格保护——后续写入永远不会撕裂一个宽字符;
  3. 对宽度歧义字符保持警觉——宁可换字符,也不把对齐交给终端的 locale。

如果你也在做终端绘图或 Markdown 渲染相关的项目,这套"宽度优先"的思路非常值得参考。更多用法与示例见 README.md。

【免费下载链接】mermaid-asciiRender Mermaid graphs inside your terminal项目地址: https://gitcode.com/GitHub_Trending/me/mermaid-ascii

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询