写备注总有时候光靠文字描述不是很清楚,这个时候特别想在文档中附个关系图。用 graphviz 的 DOT 来描述图固然不错,但是它需要额外工具来生成才能直观的查看。如果我们想在纯文本环境附上插图,使用 ascii art 是最佳选择。
我搜了一下,perl 的 Graph::Easy 是唯一能找到的支持 ascii 输出的关系图绘制软件。
但是这个东西有个问题,就是对中文支持不行,虽然文档中说完全支持 Unicode ,但显然作者并没有把汉字用在 Ascii 输出上过。
问题是这样产生的:UTF8 中,汉字是 3 字节,而对于显示来说,等宽字体的汉字是 2 倍普通字母宽。这样在排版时,你必须知道一个字符串在视觉上是多宽,才可能用 ascii 字符拼接出正确的方框和连线。
比如,这个帖子就讨论了类似的问题 。
一开始,我希望找到 Graph::Easy 库中取 label 宽度的函数,让它正确返回 label 的视觉宽度。修改后,发现还是不行。这是因为,这个东西在 ascii 输出时,是用一个 2D 的字符数组模拟了虚拟画布(framebuffer)。它认为,framebuffer 上的一个矩形区域,就应该是 w * h 个字符构成的区域。如果想进一步的让它在 framebuffer 上定位正确,还需要修改它计算坐标的地方。可是,写 framebuffer 是一个随机过程,你向左边写一个一个字符,如果是汉字的话,会改变右边的 x 坐标。也就是把右侧所有的字符都挤了一个位置。如果再重排右侧的所有数据代价就太大了,而且它的内部模块也并没有抽象出这样的定位函数。
后来我想到一个取巧的做法,再读取任何 label 串时,都把这个串处理一下,把其中的汉字全部追加一个不可能在正常文本中存在的字符 U+FFFF ,让每个汉字都真的占据两个字符位。这样的字符串处理函数原本就在模块内存在(因为它本身就要去做类似 \n 的转义),这样就可以让整个排版过程正常了。
接下来要改的是最终把 framebuffer 序列化回字符串的过程,把里面所有的 U+FFFF 都删掉,就可以正确的输出了。
ps. 本来想提个 PR ,但是 github 上的那个仓库似乎不是原作者在维护的, CPAN 上的版本也不知道怎么提 issue ,所以还是自己玩玩就行了吧。