sublime中为Markdown文档插入剪切板中的图片
sublime中为Markdown文档插入剪切板中的图片
sublime中为Markdown文档插入剪切板中的图片的插件没有合适的,找到一个不是很好用,几年前开发的,以及不维护了。于是,自己写一个吧。
插入剪切板中的图片
自己写的好处就是,各种问题都可以自己去进行修复,进行定制化的配置。这个也是的,在Macos系统下面是Retina屏,截屏保存的图片的像素是普通屏幕的大概4倍,就是长度和宽度都变成了2倍。
所以,我在粘贴图片的时候就会在普通的![]()
后面再加上原始截屏时的长度和宽度![](){width="10" height="10"}
。这样,即使在Retina屏下截取的图片也不影响,最终呈现的大小和原始的Retina一样,而不是4倍。
目前直接使用全路径,将所有的图片放在一个单独的文件夹,而不是一般的,放在.md文件的当前目录下面,这样,.md文档的目录看起来更干净一些。
由于sublime的默认包里面没有PIL包,所以,使用了外置的python来执行,需要在环境变量中加入python的路径。并且安装PIL包。
清理图片
在粘贴的过程中,有可能会截图多次,或者文章中把图片删除了,但实际上的图片并没有删除。可以把图片对应的文章中的引用扫描一下,如果没有引用了,则可以删除掉。这样,可以把不需要的图片及时清理掉。默认把清理的图片移动到backup目录,可以自己确认之后再删除。
pip3 install pillow
import sublime
import sublime_plugin
import os
import sys
import subprocess
class MarkdownImagePasteObject(object):
settings_file = 'MarkdownImagePaste.sublime-settings'
def __init__(self, *args, **kwgs):
super(MarkdownImagePasteObject, self).__init__(*args, **kwgs)
self.settings = sublime.load_settings(self.settings_file)
self.image_dir = self.settings.get("image_dir", ".images")
self.project_dir = self.settings.get("project_dir")
if not self.project_dir:
self.project_dir = "~/md"
self.project_dir = os.path.normpath(os.path.expanduser(self.project_dir))
class MarkdownImagePasteCommand(MarkdownImagePasteObject, sublime_plugin.TextCommand):
def run(self, edit):
filename = self.get_filename()
if filename is None:
sublime.error_message('Please save the file first!')
return
size = self.paste_image(filename)
# print("size:", size)
if size:
for pos in self.view.sel():
if 'text.html.markdown' in self.view.scope_name(pos.begin()):
if sys.platform == 'darwin':
width = int(size[0]) // 2
hight = int(size[1]) // 2
if width > 900:
ratio = 900.0 / width
width = 900
hight = int(hight * ratio)
self.view.insert(edit, pos.begin(), '![](%s){width="%d" height="%d"}' % (filename, width, hight))
# self.view.insert(edit, pos.begin(), '<img src="%s" width="%s" height="%s"/>' % (filename, size[0], size[1]))
else:
self.view.insert(edit, pos.begin(), "![](%s)" % filename)
else:
self.view.insert(edit, pos.begin(), "%s" % filename)
break
else:
self.view.run_command("paste")
def get_filename(self):
view = self.view
filename = view.file_name()
if filename is None:
# raise RuntimeError("Please save the file first!")
return None
else:
filename = os.path.normpath(os.path.expanduser(filename))
# create dir in current path with the name of current filename
dirname, _ = os.path.splitext(filename)
sub_dir = dirname[len(self.project_dir) + 1:]
# print("sub_dir", sub_dir)
# create new image file under currentdir/filename_without_ext/filename_without_ext%d.png
fn_without_ext = os.path.basename(dirname)
full_image_dir = os.path.join(self.project_dir, self.image_dir, sub_dir)
# print("full_image_dir", full_image_dir)
if not os.path.lexists(full_image_dir):
os.makedirs(full_image_dir)
i = 0
while True:
# absolute file path
abs_filename = os.path.join(full_image_dir, "%s%d.png" % (fn_without_ext, i))
if not os.path.exists(abs_filename):
break
i += 1
# print("save file: " + abs_filename)
return abs_filename
def paste_image(self, filename):
'''
成功:返回格式为 (width, height),失败:返回 None
'''
# 内部没有pillow的lib,用外包python执行
command = 'python3 "%s" save "%s"' % (os.path.join(os.path.dirname(__file__), 'bin/imageutils.py'), filename)
# print(command)
out = self.run_command(command)
# print("out:" + out + ":end")
if out and out[-2:-1] == "0":
return out.split("\n")[0].split(",")
else:
return None
def run_command(self, cmd):
filename = self.view.file_name()
if filename is None:
cwd = "~"
else:
cwd = os.path.dirname(filename)
# print("cmd %r" % cmd)
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd, env=os.environ)
try:
outs, errs = proc.communicate(timeout=15)
# print("outs %r %r" % (outs, proc))
except Exception:
proc.kill()
outs, errs = proc.communicate()
print("outs %r, errs %r" % (b'\n'.join(outs.split(b'\r\n')), errs))
if errs is None or len(errs) == 0:
return outs.decode()
class MarkdownImageCleanCommand(MarkdownImagePasteObject, sublime_plugin.TextCommand):
def run(self, _):
self._clean()
def _clean(self):
img_dir = os.path.join(self.project_dir, self.image_dir)
backup_dir = os.path.join(self.project_dir, self.backup_dir)
md_files = self.get_files_recursively(self.project_dir, lambda file: file.endswith(".md"))
img_files = self.get_files_recursively(img_dir, lambda file: file.endswith(".png") or file.endswith(".gif") or file.endswith(".jpg"))
used_imgs = set(self._get_used_imgs(md_files))
count = 0
for img in img_files:
if img not in used_imgs and os.path.isfile(img):
# os.remove(img)
new_img = img.replace(img_dir, backup_dir)
debug(new_img)
os.makedirs(os.path.dirname(new_img))
shutil.move(img, new_img)
count += 1
sublime.status_message(str(count) + " 张图片清理成功,已移动到backup目录")
def _get_used_imgs(self, files):
used_imgs = []
for file in files:
with open(file, 'r', encoding='utf-8') as f:
content = f.read()
imgs = re.findall(r'!\[[^\]]*?\]\(([^\)]*)\)', content)
used_imgs += imgs
return used_imgs
def get_files_recursively(self, dir, filter_func=None):
if not os.path.isdir(dir):
return list(dir)
dirlist = os.walk(dir)
result = []
for root, _, files in dirlist:
for file in files:
full_path = os.path.join(root, file)
if filter_func:
if filter_func(full_path):
result.append(full_path)
else:
result.append(full_path)
return result
bin/imageutils.py
"""
pip install pillow
"""
import sys
import os
from PIL import Image, ImageGrab
def getSize(filename):
with Image.open(filename) as im:
# print("file: %s with size: %d %d" % (file1, im.width, im.height))
return (im.width, im.height)
def saveImagefile(filename):
im = ImageGrab.grabclipboard()
if isinstance(im, Image.Image):
# retina screen should scale 0.5
# if sys.platform == 'darwin':
# im = im.resize((im.size[0] // 2, im.size[1] // 2), Image.ANTIALIAS)
print("%d,%d" % (im.size[0], im.size[1]))
im.save(filename)
return 0
else:
return 1
if __name__ == '__main__':
if len(sys.argv) == 3:
# python imageutil.py size filename
# print("begin %r" % sys.stdin.encoding)
if sys.argv[1] == 'size':
print("%d,%d" % getSize(sys.argv[2]))
elif sys.argv[1] == 'save':
print(saveImagefile(sys.argv[2]))