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]))     
posted @ 2022-04-29 17:52  yangwen0228  阅读(491)  评论(0编辑  收藏  举报