From ee9f9274a7a49994a7a879dea8d731bee995ae81 Mon Sep 17 00:00:00 2001 From: Allen Bargi Date: Tue, 20 Oct 2015 03:01:03 +0200 Subject: [PATCH] added inline image preview --- Default (Linux).sublime-keymap | 6 +- Default (OSX).sublime-keymap | 6 +- Default (Windows).sublime-keymap | 6 +- README.md | 1 + note_support.py | 106 +++++++++++++++++++++++++++++++ 5 files changed, 122 insertions(+), 3 deletions(-) diff --git a/Default (Linux).sublime-keymap b/Default (Linux).sublime-keymap index 47e01fe..d8f96af 100644 --- a/Default (Linux).sublime-keymap +++ b/Default (Linux).sublime-keymap @@ -11,7 +11,11 @@ }, { "keys": ["enter"], "command": "note_open_url", "context": - [{ "key": "selector", "operator": "equal", "operand": "text.html.markdown.note markup.underline.link.markdown" }] + [{ "key": "selector", "operator": "equal", "operand": "text.html.markdown.note meta.link.inline.markdown | text.html.markdown.note meta.link.inet.markdown | text.html.markdown.note meta.link.reference.def.markdown" }] +}, + +{ "keys": ["enter"], "command": "note_preview_image", "context": + [{ "key": "selector", "operator": "equal", "operand": "text.html.markdown.note meta.image.inline.markdown" }] }, // Notes: Jotter keymaps diff --git a/Default (OSX).sublime-keymap b/Default (OSX).sublime-keymap index 768d0c2..094cc46 100644 --- a/Default (OSX).sublime-keymap +++ b/Default (OSX).sublime-keymap @@ -11,7 +11,11 @@ }, { "keys": ["enter"], "command": "note_open_url", "context": - [{ "key": "selector", "operator": "equal", "operand": "text.html.markdown.note meta.link.inline.markdown | text.html.markdown.note meta.link.inet.markdown" }] + [{ "key": "selector", "operator": "equal", "operand": "text.html.markdown.note meta.link.inline.markdown | text.html.markdown.note meta.link.inet.markdown | text.html.markdown.note meta.link.reference.def.markdown" }] +}, + +{ "keys": ["enter"], "command": "note_preview_image", "context": + [{ "key": "selector", "operator": "equal", "operand": "text.html.markdown.note meta.image.inline.markdown" }] }, // Notes: Jotter keymaps diff --git a/Default (Windows).sublime-keymap b/Default (Windows).sublime-keymap index 47e01fe..d8f96af 100644 --- a/Default (Windows).sublime-keymap +++ b/Default (Windows).sublime-keymap @@ -11,7 +11,11 @@ }, { "keys": ["enter"], "command": "note_open_url", "context": - [{ "key": "selector", "operator": "equal", "operand": "text.html.markdown.note markup.underline.link.markdown" }] + [{ "key": "selector", "operator": "equal", "operand": "text.html.markdown.note meta.link.inline.markdown | text.html.markdown.note meta.link.inet.markdown | text.html.markdown.note meta.link.reference.def.markdown" }] +}, + +{ "keys": ["enter"], "command": "note_preview_image", "context": + [{ "key": "selector", "operator": "equal", "operand": "text.html.markdown.note meta.image.inline.markdown" }] }, // Notes: Jotter keymaps diff --git a/README.md b/README.md index cdae9b7..81b2bee 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,7 @@ To add more yaml items you can add them to the settings by modifying `note_yaml: #### Other features - **Open URLs**: place cursor on the link then press `enter` to open a url in the browser. +- **Preview images inline**: place cursor on a markdown image with inline image url and press `enter` to a preview popup of that image. You should have ST 3070 or newer for this feature to work. #### Per-project notes diff --git a/note_support.py b/note_support.py index 4e7771f..0160be9 100644 --- a/note_support.py +++ b/note_support.py @@ -1,5 +1,11 @@ import sublime, sublime_plugin import webbrowser +import urllib.request +import base64 +import io +import struct + +ST3072 = int(sublime.version()) >= 3072 class NoteOpenUrlCommand(sublime_plugin.TextCommand): @@ -13,3 +19,103 @@ def run(self, edit): def is_enabled(self): return 'Note.tmLanguage' in self.view.settings().get("syntax") + +if ST3072: + class NotePreviewImageCommand(sublime_plugin.TextCommand): + + def run(self, edit): + v = self.view + s = v.sel()[0] + link_region = v.extract_scope(s.a) + url = v.substr(link_region) + + req = urllib.request.Request(url, headers={"Range": "5000"}) + r = urllib.request.urlopen(req) + mime, w, h = self.getImageInfo(r.read()) + max_w, max_h = v.viewport_extent() + print(mime, w, h, max_w, max_h) + if 'image' in mime: + response = urllib.request.urlopen(url) + data = response.read() + b64 = base64.b64encode(data) + win_w, win_h = self.getPreviewDimensions(w, h, max_w, max_h) + print(win_w, win_h) + style = '' + html = style + '' + v.show_popup(html, max_width=win_w, max_height=win_h, location=link_region.a) + + def getPreviewDimensions(self, w, h, max_w, max_h): + margin = 100 + if w > (max_h - margin) or h > (max_h - margin): + ratio = w / (h * 1.0) + if max_w >= max_h: + height = (max_h - margin) + width = height * ratio + else: + width = (max_w - margin) + height = width / ratio + return (width, height) + else: + return (w, h) + + def getImageInfo(self, data): + data = data + size = len(data) + height = -1 + width = -1 + content_type = '' + + # handle GIFs + if (size >= 10) and data[:6] in (b'GIF87a', b'GIF89a'): + # Check to see if content_type is correct + content_type = 'image/gif' + w, h = struct.unpack(b"= 24) and data.startswith(b'\211PNG\r\n\032\n') and + (data[12:16] == b'IHDR')): + content_type = 'image/png' + w, h = struct.unpack(b">LL", data[16:24]) + width = int(w) + height = int(h) + + # Maybe this is for an older PNG version. + elif (size >= 16) and data.startswith(b'\211PNG\r\n\032\n'): + # Check to see if we have the right content type + content_type = 'image/png' + w, h = struct.unpack(b">LL", data[8:16]) + width = int(w) + height = int(h) + + # handle JPEGs + elif (size >= 2) and data.startswith(b'\377\330'): + content_type = 'image/jpeg' + jpeg = io.BytesIO(data) + jpeg.read(2) + b = jpeg.read(1) + try: + while (b and ord(b) != 0xDA): + while (ord(b) != 0xFF): b = jpeg.read(1) + while (ord(b) == 0xFF): b = jpeg.read(1) + if (ord(b) >= 0xC0 and ord(b) <= 0xC3): + jpeg.read(3) + h, w = struct.unpack(b">HH", jpeg.read(4)) + break + else: + jpeg.read(int(struct.unpack(b">H", jpeg.read(2))[0])-2) + b = jpeg.read(1) + width = int(w) + height = int(h) + except struct.error: + pass + except ValueError: + pass + + return content_type, width, height + + def is_enabled(self): + return 'Note.tmLanguage' in self.view.settings().get("syntax")