2020-06-02 23:33
#Sublime #Text #插件 #Python使用 Sublime Text(以下简称 ST)有很长时间了,一直对其没有 SVG(Scalable Vector Graphics:可缩放矢量图形)文件预览插件而耿耿于怀。因为 SVG 是一种和图像分辨率无关的矢量图形格式,易于编辑修改,在前端项目中被大量应用。ST 不能直接预览打开的 SVG 文件,开发过程中我需要经常切换到浏览器中预览 SVG 图像,一次两次还好,如果有大量的 SVG 文件,那么每次预览都要打开浏览器,实在是繁琐。在 Google 中搜索,发现这样一条 Issue,开发者并没有意图让 ST 原生支持 SVG 的预览,那么我就自力更生,来动手写一个预览插件。
首先 ST 的插件开发的基础知识是必需的,我以前写过一篇文章,通读一遍就有大概的了解。然后梳理一下开发思路,可以得知要开发的这个插件实际上就是一条管道,把解析端(SVG 解析软件)与展示端(ST)连接起来,每一个 SVG 文件从这个开发模型通过,就会实现我想要的效果。
+--------+ PNG +--------------+
SVG file --> | Parser | -----> | Sublime Text | --> Preview
+--------+ +--------------+
ST 本身没有解析渲染 SVG 的功能,所以要找一个 SVG 解析器。Goolge
并试用了一圈,发现还是大名鼎鼎的 Inkscape 好使,安装之后 Inkscape
可以通过 CLI 工具 inkscape
调用,文档在这里。展示端就是
ST 本身,ST 3 提供了一个简洁的 HTML/CSS 渲染引擎 minihtml,可以在
ST 视图面板中解析并显示 HTML 内容。遗憾的是这个引擎并不能识别
<svg>
标签,我的办法是先用 Inkscape 将 SVG 文件转码为
PNG 格式,再用 <img>
标签引用,最后在界面渲染出图像。
梳理了思路后,进入实际的开发,将重点的代码逻辑说明一下,实际的插件代码在 GitHub 上。
只有一句,将转码得到的 PNG 图像在视图面板中渲染出来,可调用 View Class
的 show_popup
方法。
'<img src="file://{}">'.format(tmp_png_path)) view.show_popup(
重点功能是调用 Inkscape 将 SVG 转码为 PNG,查阅文档知调用
inkscape --export-type=png my_file.svg
即可做到。但是经过我的测试,发现有些情况下,直接调用此命令并不能生成正确的
PNG 图像(有些 SVG 中的图像信息丢失了),所以要有一个额外的命令
inkscape --query-all
查询当前 SVG
文件中的全部图像信息,此命令可返回 <svg>
标签中所有嵌套元素的信息,例如:
MySvg,0,0,600,600
MyGroup,100,100,300,300
MyTriangle,120,120,100,80
MySquare,150,150,50,50
得到元素信息后,在转码命令中加入最外层的 svg id
就能正确渲染出 PNG 图像。命令更新为
inkscape --export-type=png --export-id=svg_id my_file.svg
。运行一次插件,这两个命令(query
和 export)都要调用,所以实际代码中抽象了一个公共方法
run_cmd
。
def run_cmd(cmd, msg):
"""
Run custom command and deal with std errors
"""
= os.popen(cmd)
stdout
= stdout.buffer.read().decode(encoding='utf8')
result = stdout.close()
stderr return result if not stderr else active_console(msg, cmd)
在用 os.popen
运行相关命令后,读取 Buffer
即可得到终端的输出,注意读取时用 UTF-8 格式解码,因为在 SVG 的 XML
描述文件中可能有非 ASCII
码,比如说中文。如果命令调用失败,close
方法会输出
1,此时让 ST 的控制台面板弹出,提示用户插件运行失败。
两次调用 Inkscape 命令很耗时间,所以应当将生成的 PNG 图像缓存起来。当用户多次预览同一 SVG 文件时,只有第一次才生成 PNG 图像,后面每次预览就返回第一次生成的图像。当用户预览新的 SVG 文件或者编辑了当前的 SVG 文件时,重新生成缓存。
def check_cached_file(name, basename, origin_name):
"""
Check SVG file in tmp folder and current folder
"""
= os.path.join(TMP_DIR, basename)
tmp_svg_path if not os.path.exists(tmp_svg_path):
return False
# It means original SVG file was modified
if not filecmp.cmp(tmp_svg_path, name):
return False
# Find special png name like : {filename}_svg{id}.png
# 'svg{id}' is generated by command 'inkscape --query-all'
= '{}_svg'.format(origin_name)
name_format
# If file is cached, just returns it's png file
= next(
cached_file_name for x in os.listdir(TMP_DIR) if x.startswith(name_format)), None)
(x
return cached_file_name
因为 太懒 沉迷于 JS/TS 语言,三天不练手生,再次捡起 Python
感觉很陌生,一路上有些磕磕绊绊的,不过,总归是完成了。现在 Visual Studio
Code 的强势崛起,让越来越多的人抛弃了
ST,不过我还是一如既往,深深地喜欢着她。当今社会,很多人在抱怨,却不能主动去解决问题,须知牢骚太盛防肠断,何不自己成为一个栽树的人呢?