写这篇文章之前,先说说我怎么碰上a2s的。有段时间我一直在维护一个内部项目的技术文档,里面全是靠+--+、| |手工画的架构图。图是挺直观,可放到正式的对外文档里总显得不够体面——像素感太强,放大还发虚。手工重绘成矢量图又实在划不来,几十张图一张张画下去,一天就没了。后来我在找“ASCII 转 SVG”的方案时翻到了a2s,试了一下,五分钟就把文档里的“豆腐块”全部洗成了清晰可缩放的矢量图,那种感觉确实很舒服。
这篇文章就围绕a2s的语法、参数和实际应用案例展开。如果你平时写技术文档、画架构图、维护 README,或者经常要拿文本形式的信息图去换一种更专业的展示形态,那这篇文章应该能帮到你。如果你是刚接触 Python 的新手也没关系,下面的内容我会尽量把每一步都拆开讲清楚。
1. 认识 a2s:它到底解决什么问题
1.1 一个拿得出手的场景
不少项目里都有这种“手绘”架构图:
+---------------------+ +---------------------+ | Web Server | <----> | Business Core | | | HTTP | | +---------------------+ +---------------------+ | | v v +---------------------+ +---------------------+ | Cache Cluster | | Database | +---------------------+ +---------------------+这段文本放在代码注释里、放在 README 里,都非常直观,跨平台也不会丢格式。但一旦你要把它放进 PPT、官网或者正式技术白皮书,纯文本的展示就有点单薄了:不能无损缩放,边框粗细不统一,更没法做颜色和样式定制。
a2s就是干这件事的。全称是ASCII to SVG,本质上是一个字符图形解析器加矢量画布生成器。它把文本里的框线、连接线、字符块识别成对应的几何元素(矩形、线段、路径),最终输出一份标准 SVG 矢量图。SVG 是纯文本格式的矢量图,可以用浏览器直接打开,也可以用代码随意改颜色、改线宽,还能放进 Word、PPT 和网页里。
1.2 它的设计思路
a2s的处理流程大致分三步:读取文本、识别图形结构、生成 SVG。识别结构这一步是核心,它依赖一个字符表(a2s.c模块)来告诉程序“哪些字符是边框、哪些是线条、哪些是普通文本内容”,再通过上下文推断这些字符组成的图形边界。
这个思路和直接用 OCR 识别图片完全不一样。OCR 是把光栅图像转成文字和结构,a2s是把已经结构化的文本重新解析成几何描述。所以它对输入格式有一定要求——文本中的框线要对齐,字符要规整。如果源文件里框线七扭八歪,它自然也没办法变出好图来。
2. 安装与第一行代码
2.1 安装 a2s
安装很简单,用 pip 就能搞定:
pip install a2s如果网络环境不太友好,可以换国内镜像:
pip install a2s -i https://pypi.tuna.tsinghua.edu.cn/simple装完之后验证一下:
python -c "import a2s; print(a2s.__version__)"能打印出版本号就说明基础环境没问题。
注意:a2s 依赖 lxml 和 Pillow。如果你用的是非常精简的 Python 环境(比如某些 Docker 基础镜像),可能需要单独装一下:
pip install lxml pillow。后面convert_image转 PNG 的时候会用到 Pillow。
2.2 第一次转换
先准备一个最简单的文本文件input.txt:
+-----+ | | | 1 | | | +-----+然后写三行代码:
import a2s a2s.convert('input.txt', 'output.svg')打开生成的output.svg,你会在浏览器里看到一个规整的矩形,中间有个数字1。这个矩形其实是 a2s 识别了+-----+的边框结构后生成的 SVG<rect>元素。
如果不想指定输出路径,a2s 也支持只传源文件:
a2s.convert('input.txt')不同版本的默认行为可能略有差异,有的会生成同名.svg文件,有的会把结果直接返回。我的建议是始终显式传第二个参数,这样脚本的可控性最强,也避免不同环境下的行为差异坑到你。
2.3 顺手把 SVG 转成 PNG
很多场景下我们不只需要 SVG,还需要 PNG 这种位图格式——比如要插入到某些不支持 SVG 的内部 Wiki。a2s 提供了一个配套方法:
a2s.convert_image('output.svg', 'output.png')这个方法底层是用 Pillow 把 SVG 渲染成一张 PNG 图片。生成出来的output.png默认会保留透明背景,直接放进文档里很干净。
3. 核心语法与 API 全解
3.1 convert:从文本到 SVG
convert是 a2s 最核心的入口。基本签名是:
a2s.convert(source, target=None)其中source是输入文件路径,target是输出 SVG 文件路径。不传 target 时按默认逻辑处理。
实际使用时,我更推荐一种写法:
import a2s result = a2s.convert('架构图.txt', '架构图.svg') print(result) # 返回包含转换结果信息的对象从调用方式上可以看出来,a2s.convert不直接返回 SVG 字符串,它更像个转换驱动器,把文件读进来、处理、再把结果写到 target。如果你要在内存里直接拿结果而不落盘,一般做法是改用底层模块接口,或者先写到临时文件再处理。
另外一个使用技巧:convert方法处理的是文件,不是字符串。如果你手上只有一段已经写好的 ASCII art 字符串,可以先把字符串写到临时文件再交给 a2s。虽然多了一步,但流程最稳,不需要挖底层 API。
3.2 convert_image:从 SVG 到位图
convert_image负责把 SVG 转成 PNG 等位图格式:
a2s.convert_image('output.svg', 'output.png')这里有个小细节:生成的 PNG 尺寸默认取自 SVG 自身的宽高比例。如果后面发现生成的图片过大或过小,不要急着在 a2s 里找缩放参数,最通用的做法是转换完成后用 Pillow 二次处理:
from PIL import Image img = Image.open('output.png') img = img.resize((600, 400), Image.Resampling.LANCZOS) img.save('resized.png')3.3 命令行方式
如果你的工作流里全是 Shell 脚本,用命令行更顺手。部分版本安装后会提供命令行入口,你可以直接:
a2s input.txt output.svg如果直接用a2s命令没生效,可以用模块方式调用:
python -m a2s input.txt output.svg具体命令名称在不同分支版本里有一点差别,拿不准的时候跑一下--help就知道了。命令行交互适合批量转换,做成 Shell 循环,一条命令处理一整个目录,非常省事。
4. 参数模块 tune 详解
4.1 为什么需要参数调整
ASCII art 里的字符宽度不是统一的。英文小写i和W在大多数等宽字体里宽度一致,但如果你用的字体不是严格等宽,或者输入文本里混入了全角中文(一个汉字约占两个英文字符宽度),a2s 在把字符位置换算成 SVG 坐标时就会出现偏差,表现成图形错位、框线对不齐。
a2s.tune就是用来干“微调”这件事的模块。通过设置参数,让解析器知道当前文本使用的是什么样的字符网格,从而更准确地映射坐标。
4.2 常用参数配置
最常见的做法是通过set_params设置全局参数:
from a2s import tune tune.set_params( doc_w=960, # 输出画布宽度 doc_h=720, # 输出画布高度 char_w=12, # 单个字符宽度 char_h=24 # 单个字符高度 )doc_w和doc_h代表最终 SVG 画布的尺寸,决定了生成的矢量图整体大小。char_w和char_h则是解析时使用的字符网格尺寸,它们影响图形元素的相对坐标。
不同版本对参数名可能有一点点兼容差异,如果你安装的版本提示参数不识别,用下面的方式先看看模块支持什么参数:
import inspect from a2s import tune print(inspect.signature(tune.set_params))这个方法我在排查参数问题的时候经常用,极其灵验。
4.3 等宽模式与字符对齐
除了set_params,还有一个值得单独拎出来讲的是等宽模式。如果你的原始文本里用了大量|、-、+来画框线,这些字符本身的宽度在常见字体里是一致的,但内容部分如果长短不一,就会导致整个图形右侧对不齐。
a2s 提供类似EqualWidth的开关,用来告诉解析器:请把所有字符按等宽处理,这样框线就能严格对齐。
from a2s import tune tune.EqualWidth(True)开启之后,解析器会忽略字体本身的度量差异,强制把每个字符都放进相同宽度的网格里。这非常适合那些手写规整、刻意对齐过的 ASCII art。
但要注意:如果你的源文本里混了全角中文,强行开启等宽模式反而可能让中文左右产生空隙或不自然拉伸。因为全角汉字天然就是双倍宽度,你把它和非等宽字符塞进同一个格子,图形是变整齐了,可中文字符的观感对不齐了。这种情况下,我建议优先保证文本自身严格对齐,再用等宽模式微调。
4.4 参数选型的几个实操建议
我整理了一套在项目里实际用过的参数选择规则,直接抄作业也行:
| 场景 | 推荐做法 |
|---|---|
| 纯英文/数字的框线图 | 开启EqualWidth(True),char_w=8,char_h=16 |
| 含中文注释的架构图 | 关闭等宽开关,char_w=12,char_h=24,用全角字符对齐 |
| 要生成高分辨率 PNG | 不急着调画布,先转出 SVG 再二次缩放 |
| 复杂嵌套框图 | 优先处理源文本对齐,不要指望参数能修复错位 |
记住一个原则:参数是锦上添花,不是雪中送炭。源文本里框线没对齐的话,怎么调参数结果都是歪的。
5. 实际应用案例
5.1 案例一:把代码注释里的架构图转成文档素材
我在维护一个老项目时,发现源码里有一坨架构注释图,画的是消息队列的消费链路。这段注释非常清晰,但要想放进对外文档就“拿不出手”。我当时做了这几步:
先把源码注释里的图复制出来,保存成queue_arch.txt:
+-----------+ +-----------+ +-----------+ | Producer | topic | Broker | part | Consumer | | P1 | -------> | B1/B2 | ------> | C1 | +-----------+ +-----------+ +-----------+然后执行转换脚本:
import a2s from a2s import tune # 英文框图,开启等宽模式最稳妥 tune.EqualWidth(True) a2s.convert('queue_arch.txt', 'queue_arch.svg') a2s.convert_image('queue_arch.svg', 'queue_arch.png')生成的queue_arch.svg直接拖进绘图软件还能继续编辑,框、线、文字全部是独立元素。我把文字部分改成品牌色后,整张图瞬间就有了“正式文档”的气质。
这个场景是我认为 a2s 价值最明显的地方:把埋在代码里的图形资产,一键“提纯”成可复用素材。
5.2 案例二:批量转换 docs 目录下的所有文本图
后来我发现团队 docs 目录下有一堆.txt架构草图,有些还带着版本后缀。人工一个个处理太蠢,我就写了个批量脚本:
import os import a2s from a2s import tune tune.EqualWidth(True) src_dir = './docs/raw' dst_dir = './docs/svg' os.makedirs(dst_dir, exist_ok=True) for filename in os.listdir(src_dir): if not filename.endswith('.txt'): continue src_path = os.path.join(src_dir, filename) dst_path = os.path.join(dst_dir, filename.replace('.txt', '.svg')) try: a2s.convert(src_path, dst_path) print(f'[OK] {filename} -> {os.path.basename(dst_path)}') except Exception as e: print(f'[FAIL] {filename}: {e}')这个脚本看起来简单,但有几个点我在实际跑的时候才踩到:
- 文件名包含中文时,个别旧版本在解析路径时可能出问题。给代码开头加上
# -*- coding: utf-8 -*-,或者统一转成绝对路径再用,基本能规避。 - 转换失败不会导致整个脚本崩掉,因为包了一层 try/except,打日志比中断强。
- 如果某个文件转换后图明显错位,我的习惯是先定位源文件是否对齐,而不是盲目调参数。
批量处理后的所有 SVG 我统一扔进一个assets目录,文档需要时直接引用。省下来的时间至少是一下午。
5.3 案例三:在 Jupyter Notebook 里展示技术图
写技术方案的时候,我经常用 Jupyter Notebook 做草稿,既写思路又贴图。a2s 生成的 SVG 在 Notebook 里展示也很方便:
import a2s from IPython.display import SVG, display # 先生成 SVG 文件 a2s.convert('design.txt', 'design.svg') # 再在 Notebook 里渲染出来 display(SVG(filename='design.svg'))执行完这个单元格,生成的架构图会以内联 SVG 的形式直接出现在 Notebook 里。好处是它是矢量图,放大不糊,而且我可以在同一个 Notebook 里反复修改文本图、重新生成、即时对比。这种“文本改一行,图片跟着变”的工作流,比打开画图工具改半天高效太多了。
如果你还想在 Notebook 里顺手导出 PNG,加一行就行:
a2s.convert_image('design.svg', 'design.png')Notebook 里可以直接插图片,适合最后把方案贴到在线文档的场景。
5.4 案例四:CI 流水线里自动生成 API 结构图
再扩展一步。我们有个服务模块的接口文档是自动生成的,里面有一张 API 结构图。以前的流程是开发手画后截图上传,经常忘了更新。后来我用 a2s 做了自动化:
在仓库里维护一个api_structure.txt,代码合并时,CI 跑一个脚本文件:
import a2s from a2s import tune tune.EqualWidth(True) a2s.convert('api_structure.txt', 'public/api-structure.svg') a2s.convert_image('public/api-structure.svg', 'public/api-structure.png')这样,任何人更新了 API 结构图,只要同步改一下文本文件,提交后自动生成最新的矢量图和位图,文档站点直接引用这两个文件即可。文本文件本身就是代码的一部分,走 Code Review 流程,改了什么一目了然。这个思路把“画图”彻底变成了“改文本”,对团队协作来说非常友好。
6. 常见问题与排查技巧
6.1 转换后图形错位或框线断裂
表现:生成的 SVG 里,矩形边缘该接上的地方没接上,或者线条歪斜。
原因:几乎都是源文本没有对齐。看下面这两个例子:
对不齐的:
+-----+ | | +----+对齐的:
+------+ | | +------+a2s 的图形识别依赖框线在坐标上的连续性,只要有一行少了几个空格,矩形就会少一条边或多一个偏移。
排查方法:先用支持等宽字体的编辑器(VS Code、Sublime 都行)打开源文件,肉眼检查每一行右侧是否对齐。也可以用脚本检查每行长度:
with open('input.txt', 'r', encoding='utf-8') as f: lines = f.readlines() lengths = {len(line.rstrip()) for line in lines} print(lengths)如果集合里元素个数大于 1,说明各行长度不一致,先手工对齐再说。
6.2 中文字符导致的宽度问题
表现:框内中文内容显示正常,但框线或相邻结构错位。
原因:中文全角字符宽度大约是英文的两倍。如果char_w参数按英文字符宽度设置,遇到中文就会把坐标计算偏。
解决办法:我的经验是给char_w设置成英文的两倍,比如英文字符网格是 8,那中文场景就设 12 或 16,具体要看字体。更稳的办法是源文本里不让中文参与框线的坐标计算——中文字符只在矩形内部作为文本内容出现,框线完全用+、-、|这些 ASCII 字符绘制。
6.3 SVG 字体依赖导致跨平台显示不一致
表现:同一个 SVG,在自己机器上浏览器里显示正常,发给同事后文字字体变了,排版乱了。
原因:SVG 本身不内嵌字体,它依赖系统字体。a2s 生成的 SVG 里,文字通常使用默认字体族(比如sans-serif),在不同操作系统上渲染结果不同。
解决办法:如果你对字体有强要求,生成 SVG 后直接用文本编辑器打开它,在<text>元素上把font-family改成指定字体,比如:
<text font-family="'Courier New', monospace">Producer</text>或者直接在生成后写一个小脚本,用字符串替换统一加字体:
with open('output.svg', 'r', encoding='utf-8') as f: content = f.read() content = content.replace( '<text', '<text font-family="Arial, sans-serif"' ) with open('output.svg', 'w', encoding='utf-8') as f: f.write(content)6.4 转换后 SVG 整体太小或太大
表现:SVG 在浏览器里打开,图形小得像邮票,或者大得溢出屏幕。
原因:默认画布尺寸是按字符网格和文本行数推算出来的,有时候源文本行数很多,但每行宽度很窄,导致画布比例失衡。
解决办法:设置画布尺寸。
from a2s import tune tune.set_params(doc_w=1024, doc_h=768)需要注意,这改变的是整体画布,不是缩放倍率。如果你想要的只是等比缩放,更推荐生成后用 Pillow 或浏览器再处理,因为改画布会影响图形元素坐标计算,可能导致间距变化。
7. 我的实操心得与建议
最后分享几个我自己在实践中总结出来的经验,不一定写进官方文档,但很管用。
第一,源文本的“干净度”决定了a2s的上限。你花十分钟手工整理一个对齐的 ASCII art,比花一小时调参更有效。我的习惯是:任何交给 a2s 的文本,先放在等宽字体下检查一遍,特别留意框线转角处有没有多余空格,短线有没有漏接。
第二,别让 a2s 承担太多“审美工作”。a2s 擅长的是把文本结构精确地转成矢量图形,但配色、字体、边框粗细这些审美层面的东西,生成之后再处理反而更顺手。SVG 本身就是文本格式,你可以用脚本批量改颜色,也可以拖进绘图工具二次加工。拿到的是一份干净的几何图形,后面想怎么打扮都随你。
第三,把文本图当作源码来管理。一旦你习惯了“文本 -> SVG”的自动化流程,你会自然地想给所有架构图维护一份文本源文件。文本文件不但在 Git 里可 diff,还能被代码评审。以后图变了,评审的人看一眼文本 diff,马上知道动了什么。这是传统绘图工具完全做不到的协作优势。
第四,a2s 不是万能的。过于复杂的图形,比如有曲线箭头、多层嵌套圆角框、颜色渐变的架构图,用 a2s 硬生成会非常痛苦,效果也不好看。我的建议是:简单规整的框图、网络拓扑、模块关系图,用 a2s 自动化;复杂的设计稿,老老实实用专门工具画。工具没有高低之分,关键是选对场景。
如果你手头正好有一堆文本框图,不妨试着跑一遍 a2s。它可能不会帮你画出一张惊艳的海报,但一定能帮你把“能看”变成“好用”,把藏在代码里的技术图转化成真正可复用的项目资产。