• sublime中Markdown文档中的图片预览


    sublime中Markdown文档中的图片预览

    MarkdownImages(废弃)

    把这个插件打开的时候,用一段时间,sublime中的图片就会不断的闪动,鼠标也跳来跳去,然后,僵死,然后,不可用。看了一下原因,可能是这个插件里面的图片是不断删掉又重新插入,和sublime自己的输入触发更新图片,重新计算图片的大小和文字高低比较频繁有关系。

    另外,里面有一个很重要的误用是,phantom里面的key,在sublime中,这个key是可以多个图片共用的,而作者把这个key理解为每个图片要生成一个key,这样,就需要自己用全局变量去保存这个key,一旦这个key丢失了,就图片都清理不掉了。实际上,我们用view.id()来标识这个文件里面的所有的图片就可以了。这样,就可以很方便的把本文件的图片都清掉,也不担心全局变量丢失之后清理不掉图片的问题了。

    修改方案

    解决方案就是在插入图片的时候进行预览,因为大多数时候,在写作时,我们只关心插入图片的时候,图片是否是正确的,一旦按了保存键,就把所以的图片清空掉,这样,既不需要缓存大量的图片,也不会在写作过程中,显示图片,导致大量的鼠标跳动和卡顿。

    这样真是非常顺畅,如果想要预览一下图片,那手动执行markdown_image_show显示所有的图片,再保存一下就清掉了所有图片。这样就比较符合高效实用的目的了。

    代码如下

    import sublime
    import sublime_plugin
    import os
    import sys
    import subprocess
    
    import struct
    import imghdr
    import base64
    import urllib.request
    import urllib.parse
    import io
    import re
    
    DEBUG = False
    
    def debug(*args, **kwargs):
        if DEBUG:
            print(*args, **kwargs)
    
    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()):
                        width = int(size[0])
                        hight = int(size[1])
                        if sys.platform == 'darwin': # Retina screen
                            width = width // 2
                            hight = hight // 2
    
                        max_width = int(self.settings.get("image_max_width", 900))
                        if width > max_
                            ratio = max_width / width
                            width = max_width
                            hight = int(hight * ratio)
                        self.view.insert(edit, pos.begin(), '![](%s){width="%d" height="%d"}' % (filename, width, hight))
                    else:
                        self.view.insert(edit, pos.begin(), "%s" % filename)
                    break
                # show the pasted image
                ImageHandler.show_images_for_filename(self.view, filename)
            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 MarkdownImageShowCommand(MarkdownImagePasteObject, sublime_plugin.TextCommand):
        """
        Show local images inline.
        """
        def run(self, edit, **kwargs):
            show_local = kwargs.get('show_local', True)
            show_remote = kwargs.get('show_remote', True)
            max_width = self.settings.get("image_max_width", 900)
            ImageHandler.hide_images(self.view)
            ImageHandler.show_images_for_custome(self.view)
    
    class MarkdownImageHideCommand(MarkdownImagePasteObject, sublime_plugin.TextCommand):
        """
        Hide all shown images.
        """
        def run(self, edit):
            ImageHandler.hide_images(self.view)
    
    class MarkdownImageListener(sublime_plugin.EventListener):
        def on_post_save(self, view):
            print("close images")
            ImageHandler.hide_images(view)
    
        def on_close(self, view):
            print("close images")
            ImageHandler.hide_images(view)
    
    class ImageHandler:
        """
        Static class to bundle image handling.
        """
    
        @staticmethod
        def show_images_for_standard(view, max_width=None, show_local=True, show_remote=False, base_path=""):
            selector = 'markup.underline.link.image.markdown'
            ImageHandler.show_images_for_selector(view, selector, max_width, show_local, show_remote, base_path)
    
        @staticmethod
        def show_images_for_selector(view, selector, max_width=None, show_local=True, show_remote=False, base_path=""):
            img_regs = view.find_by_selector(selector)
            ImageHandler.show_images(view, img_regs, max_width, show_local, show_remote, base_path)
    
        @staticmethod
        def show_images_for_custome(view, max_width=None, base_path=""):
            selector = 'markup.underline.link.image.markdown'
            img_regs = view.find_by_selector(selector)
            pattern = r'<!-- !\[[^\]]*?\]\(([^\\)]*?)\).*? -->'
            uploaded_images = view.find_all(pattern)
            for img_reg in uploaded_images:
                m = re.match(pattern, view.substr(img_reg))
                if m:
                    img_regs.append(view.find(m.groups()[0], start_pt=img_reg.a))
    
            ImageHandler.show_images(view, img_regs, max_width, True, True, base_path)
    
        @staticmethod
        def show_images_for_filename(view, filename, max_width=None, base_path=""):
            img_regs = view.find_all(filename)
            ImageHandler.show_images(view, img_regs, max_width, True, True, base_path)
    
        @staticmethod
        def show_images(view, img_regs, max_width=None, show_local=True, show_remote=False, base_path=""):
            '''
            img_regs: [(0, 10), (20, 25)] # 图片的起始位置列表
            '''
            debug("show_images")
            if not show_local and not show_remote and not img_regs:
                debug("doing nothing")
                return
            # Note: Excessive size will cause the ST3 window to become blank
            # unrecoverably. 900 apperas to be a safe limit,
            # but can possibly go higher.
            if not max_width or max_width < 0:
                max_width = 900
    
            debug("img_regs", img_regs)
            # Handling space characters in image links
            # Image links not enclosed in <> that contain spaces
            # are parsed by sublime as multiple links instead of one.
            # Example: "<!-- ![](my file.png) -->" gets parsed as two links: "my" and "file.png".
            # We detect when two links are separated only by spaces and merge them
            indexes_to_merge = []
            for i, (left_reg, right_reg) in enumerate(zip(img_regs, img_regs[1:])):
                inter_region = sublime.Region(left_reg.end(), right_reg.begin())
                if (view.substr(inter_region)).isspace():
                    # the inter_region is all spaces
                    # Noting that left and right regions must be merged
                    indexes_to_merge += [i+1]
            new_img_regs = []
            for i in range(len(img_regs)):
                if i in indexes_to_merge:
                    new_img_regs[-1] = new_img_regs[-1].cover(img_regs[i])
                else:
                    new_img_regs += [img_regs[i]]
            img_regs = new_img_regs
    
            for region in reversed(img_regs):
                ttype = None
                rel_p = view.substr(region)
    
                # If an image link is enclosed in <> to tolerate spaces in it,
                # then the > appears at the end of rel_p for some reason.
                # This character makes the link invalid, so it must be removed
                if rel_p[-1] == '>':
                    rel_p = rel_p[0:-1]
    
                # (Windows) cutting the drive letter from the path,
                # otherwise urlparse interprets it as a scheme (like 'file' or 'http')
                # and generates a bogus url object like:
                # url= ParseResult(scheme='c', netloc='', path='/path/image.png', params='', query='', fragment='')
                drive_letter, rel_p = os.path.splitdrive(rel_p)
                url = urllib.parse.urlparse(rel_p)
                if url.scheme and url.scheme != 'file':
                    if not show_remote:
                        continue
    
                    # We can't render SVG images, so skip the request
                    # Note: not all URLs that return SVG end with .svg
                    # We could use a HEAD request to check the Content-Type before
                    # downloading the image, but the size of an SVG is typically
                    # so small to not be worth the extra request
                    if url.path.endswith('.svg'):
                        continue
    
                    debug("image url", rel_p)
                    try:
                        data = urllib.request.urlopen(rel_p)
                    except Exception as e:
                        debug("Failed to open URL {}:".format(rel_p), e)
                        continue
    
                    try:
                        data = data.read()
                    except Exception as e:
                        debug("Failed to read data from URL {}:".format(rel_p), e)
                        continue
    
                    try:
                        w, h, ttype = get_image_size(io.BytesIO(data))
                    except Exception as e:
                        msg = "Failed to get_image_size for data from URL {}"
                        debug(msg.format(rel_p), e)
                        continue
    
                    FMT = u'''
                        <img src="data:image/{}" class="centerImage" {}>
                    '''
                    b64_data = base64.encodebytes(data).decode('ascii')
                    b64_data = b64_data.replace('\n', '')
    
                    img = "{};base64,{}".format(ttype, b64_data)
                else:
                    if not show_local:
                        continue
    
                    # Convert relative paths to be relative to the current file
                    # or project folder.
                    # NOTE: if the current file or project folder can't be
                    # determined (e.g. if the view content is not in a project and
                    # hasn't been saved), then it will anchor to /.
                    path = url.path
    
                    # Force paths to be prefixed with base_path if it was provided
                    # in settings.
                    if base_path:
                        path = os.path.join(base_path, path)
    
                    if not os.path.isabs(path):
                        folder = get_path_for(view)
                        path = os.path.join(folder, path)
                    path = os.path.normpath(path)
                    # (Windows) Adding back the drive letter that was cut from the path before
                    path = drive_letter + path
    
                    url = url._replace(scheme='file', path=path)
    
                    FMT = '''
                        <img src="{}" class="centerImage" {}>
                    '''
                    try:
                        w, h, ttype = get_file_image_size(path)
                        debug(w,h,ttype)
                    except Exception as e:
                        debug("Failed to load {}:".format(path), e)
                        continue
                    img = urllib.parse.urlunparse(url)
    
                    # On Windows, urlunparse adds a third slash after 'file://' for some reason
                    # This breaks the image url, so it must be removed
                    # splitdrive() detects windows because it only returns something if the
                    # path contains a drive letter
                    if os.path.splitdrive(path)[0]:
                        img = img.replace('file:///', 'file://', 1)
    
                if not ttype:
                    debug("unknown ttype")
                    continue
    
                # If only width or height are provided, scale the other dimension
                # properly
                # Width defined in custom size should override max_width
                line_region = view.line(region)
                imgattr = check_imgattr(view, line_region, region)
                assert w is not None and h is not None
                if not imgattr and w > 0 and h > 0:
                    if max_width and w > max_
                        m = max_width / w
                        h = int(m * h)
                        w = max_width
                    imgattr = 'width="{}" height="{}"'.format(w, h)
    
                # Force the phantom image view to append past the end of the line
                # Otherwise, the phantom image view interlaces in between
                # word-wrapped lines
                line_region.a = line_region.b
    
                debug("region", region)
                debug("line_region", line_region)
    
                key = 'view-' + str(view.id())
                html_img = FMT.format(img, imgattr)
                debug("Creating phantom", url)
                view.add_phantom(key, sublime.Region(line_region.b), html_img, sublime.LAYOUT_BLOCK)
    
        @staticmethod
        def hide_images(view):
            key = 'view-' + str(view.id())
            view.erase_phantoms(key)
    
    def check_imgattr(view, line_region, link_region):
        # find attrs for this link
        full_line = view.substr(line_region)
        link_till_eol = full_line[link_region.a - line_region.a:]
        # find attr if present
        m = re.match(r'.*\)\{(.*)\}', link_till_eol)
        if m:
            return m.groups()[0]
        return ''
    
    
    def get_file_image_size(img):
        with open(img, 'rb') as f:
            return get_image_size(f)
    
    
    def get_image_size(f):
        """
        Determine the image type of img and return its size.
        """
        head = f.read(24)
        ttype = None
    
        debug(str(head))
        debug(str(head[:4]))
        debug(head[:4] == b'<svg')
    
        if imghdr.what('', head) == 'png':
            debug('detected png')
            ttype = "png"
            check = struct.unpack('>i', head[4:8])[0]
            if check != 0x0d0a1a0a:
                return None, None, ttype
            width, height = struct.unpack('>ii', head[16:24])
        elif imghdr.what('', head) == 'gif':
            debug('detected gif')
            ttype = "gif"
            width, height = struct.unpack('<HH', head[6:10])
        elif imghdr.what('', head) == 'jpeg':
            debug('detected jpeg')
            ttype = "jpeg"
            try:
                f.seek(0)  # Read 0xff next
                size = 2
                ftype = 0
                while not 0xc0 <= ftype <= 0xcf:
                    f.seek(size, 1)
                    byte = f.read(1)
                    while ord(byte) == 0xff:
                        byte = f.read(1)
                    ftype = ord(byte)
                    size = struct.unpack('>H', f.read(2))[0] - 2
                # SOFn block
                f.seek(1, 1)  # skip precision byte.
                height, width = struct.unpack('>HH', f.read(4))
            except Exception as e:
                debug("determining jpeg image size failed", e)
                return None, None, ttype
        elif head[:4] == b'<svg':
            debug('detected svg')
            # SVG is not rendered by ST3 in phantoms.
            # The SVG would need to be rendered as png/jpg separately, and its data
            # placed into the phantom
            return None, None, None
        else:
            debug('unable to detect image')
            return None, None, None
        return width, height, ttype
    
    
    def get_path_for(view):
        """
        Returns the path of the current file in view.
        Returns / if no path is found
        """
        if view.file_name():
            return os.path.dirname(view.file_name())
        if view.window().project_file_name():
            return os.path.dirname(view.window().project_file_name())
        return '/'
    
    
  • 相关阅读:
    eclipse工具
    Tag
    JSP模版
    Eclipse断点调试
    JavaBean
    验证码设计
    在IE中提示404错误
    序列化与反序列化
    文件编码问题
    强类型,弱类型和推断类型
  • 原文地址:https://www.cnblogs.com/yangwen0228/p/16219139.html
Copyright © 2020-2023  润新知