Skip to content

Commit ee9f927

Browse files
committed
added inline image preview
1 parent f7edacb commit ee9f927

5 files changed

+122
-3
lines changed

Default (Linux).sublime-keymap

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111
},
1212

1313
{ "keys": ["enter"], "command": "note_open_url", "context":
14-
[{ "key": "selector", "operator": "equal", "operand": "text.html.markdown.note markup.underline.link.markdown" }]
14+
[{ "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" }]
15+
},
16+
17+
{ "keys": ["enter"], "command": "note_preview_image", "context":
18+
[{ "key": "selector", "operator": "equal", "operand": "text.html.markdown.note meta.image.inline.markdown" }]
1519
},
1620

1721
// Notes: Jotter keymaps

Default (OSX).sublime-keymap

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111
},
1212

1313
{ "keys": ["enter"], "command": "note_open_url", "context":
14-
[{ "key": "selector", "operator": "equal", "operand": "text.html.markdown.note meta.link.inline.markdown | text.html.markdown.note meta.link.inet.markdown" }]
14+
[{ "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" }]
15+
},
16+
17+
{ "keys": ["enter"], "command": "note_preview_image", "context":
18+
[{ "key": "selector", "operator": "equal", "operand": "text.html.markdown.note meta.image.inline.markdown" }]
1519
},
1620

1721
// Notes: Jotter keymaps

Default (Windows).sublime-keymap

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111
},
1212

1313
{ "keys": ["enter"], "command": "note_open_url", "context":
14-
[{ "key": "selector", "operator": "equal", "operand": "text.html.markdown.note markup.underline.link.markdown" }]
14+
[{ "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" }]
15+
},
16+
17+
{ "keys": ["enter"], "command": "note_preview_image", "context":
18+
[{ "key": "selector", "operator": "equal", "operand": "text.html.markdown.note meta.image.inline.markdown" }]
1519
},
1620

1721
// Notes: Jotter keymaps

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ To add more yaml items you can add them to the settings by modifying `note_yaml:
141141
#### Other features
142142
- **Open URLs**: place cursor on the link then press `enter` to open a url in
143143
the browser.
144+
- **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.
144145

145146
#### Per-project notes
146147

note_support.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import sublime, sublime_plugin
22
import webbrowser
3+
import urllib.request
4+
import base64
5+
import io
6+
import struct
7+
8+
ST3072 = int(sublime.version()) >= 3072
39

410

511
class NoteOpenUrlCommand(sublime_plugin.TextCommand):
@@ -13,3 +19,103 @@ def run(self, edit):
1319

1420
def is_enabled(self):
1521
return 'Note.tmLanguage' in self.view.settings().get("syntax")
22+
23+
if ST3072:
24+
class NotePreviewImageCommand(sublime_plugin.TextCommand):
25+
26+
def run(self, edit):
27+
v = self.view
28+
s = v.sel()[0]
29+
link_region = v.extract_scope(s.a)
30+
url = v.substr(link_region)
31+
32+
req = urllib.request.Request(url, headers={"Range": "5000"})
33+
r = urllib.request.urlopen(req)
34+
mime, w, h = self.getImageInfo(r.read())
35+
max_w, max_h = v.viewport_extent()
36+
print(mime, w, h, max_w, max_h)
37+
if 'image' in mime:
38+
response = urllib.request.urlopen(url)
39+
data = response.read()
40+
b64 = base64.b64encode(data)
41+
win_w, win_h = self.getPreviewDimensions(w, h, max_w, max_h)
42+
print(win_w, win_h)
43+
style = '<style>body, html {margin: 0; padding: 0}</style>'
44+
html = style + '<img height="' + str(win_h) + '" width="' + str(win_w) + '" src="data:image/png;base64,' + b64.decode('utf-8') + '">'
45+
v.show_popup(html, max_width=win_w, max_height=win_h, location=link_region.a)
46+
47+
def getPreviewDimensions(self, w, h, max_w, max_h):
48+
margin = 100
49+
if w > (max_h - margin) or h > (max_h - margin):
50+
ratio = w / (h * 1.0)
51+
if max_w >= max_h:
52+
height = (max_h - margin)
53+
width = height * ratio
54+
else:
55+
width = (max_w - margin)
56+
height = width / ratio
57+
return (width, height)
58+
else:
59+
return (w, h)
60+
61+
def getImageInfo(self, data):
62+
data = data
63+
size = len(data)
64+
height = -1
65+
width = -1
66+
content_type = ''
67+
68+
# handle GIFs
69+
if (size >= 10) and data[:6] in (b'GIF87a', b'GIF89a'):
70+
# Check to see if content_type is correct
71+
content_type = 'image/gif'
72+
w, h = struct.unpack(b"<HH", data[6:10])
73+
width = int(w)
74+
height = int(h)
75+
76+
# See PNG 2. Edition spec (http://www.w3.org/TR/PNG/)
77+
# Bytes 0-7 are below, 4-byte chunk length, then 'IHDR'
78+
# and finally the 4-byte width, height
79+
elif ((size >= 24) and data.startswith(b'\211PNG\r\n\032\n') and
80+
(data[12:16] == b'IHDR')):
81+
content_type = 'image/png'
82+
w, h = struct.unpack(b">LL", data[16:24])
83+
width = int(w)
84+
height = int(h)
85+
86+
# Maybe this is for an older PNG version.
87+
elif (size >= 16) and data.startswith(b'\211PNG\r\n\032\n'):
88+
# Check to see if we have the right content type
89+
content_type = 'image/png'
90+
w, h = struct.unpack(b">LL", data[8:16])
91+
width = int(w)
92+
height = int(h)
93+
94+
# handle JPEGs
95+
elif (size >= 2) and data.startswith(b'\377\330'):
96+
content_type = 'image/jpeg'
97+
jpeg = io.BytesIO(data)
98+
jpeg.read(2)
99+
b = jpeg.read(1)
100+
try:
101+
while (b and ord(b) != 0xDA):
102+
while (ord(b) != 0xFF): b = jpeg.read(1)
103+
while (ord(b) == 0xFF): b = jpeg.read(1)
104+
if (ord(b) >= 0xC0 and ord(b) <= 0xC3):
105+
jpeg.read(3)
106+
h, w = struct.unpack(b">HH", jpeg.read(4))
107+
break
108+
else:
109+
jpeg.read(int(struct.unpack(b">H", jpeg.read(2))[0])-2)
110+
b = jpeg.read(1)
111+
width = int(w)
112+
height = int(h)
113+
except struct.error:
114+
pass
115+
except ValueError:
116+
pass
117+
118+
return content_type, width, height
119+
120+
def is_enabled(self):
121+
return 'Note.tmLanguage' in self.view.settings().get("syntax")

0 commit comments

Comments
 (0)