Skip to content

Commit

Permalink
Viewing images works for local files
Browse files Browse the repository at this point in the history
We have to make sure that everything is converted to base64 (and
replaced in the src attribute) because otherwise ST3 messes up the
height of the images
  • Loading branch information
math2001 committed Nov 14, 2019
1 parent 5738f6b commit bae26fc
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 16 deletions.
23 changes: 15 additions & 8 deletions MarkdownLivePreview.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import os.path
import sublime
import sublime_plugin

from .lib.markdown2 import Markdown
from functools import partial

from .markdown2html import markdown2html
from .utils import *

def plugin_loaded():
Expand Down Expand Up @@ -85,8 +88,6 @@ def is_enabled(self):

class MarkdownLivePreviewListener(sublime_plugin.EventListener):

markdowner = Markdown()

phantom_sets = {
# markdown_view.id(): phantom set
}
Expand Down Expand Up @@ -161,17 +162,23 @@ def on_modified_async(self, markdown_view):
self._update_preview(markdown_view)

def _update_preview(self, markdown_view):
print('update markdown view', markdown_view.is_loading())
# if the buffer id is 0, that means that the markdown_view has been closed
# This check is needed since a this function is used as a callback for when images
# are loaded from the internet (ie. it could finish loading *after* the user
# closes the markdown_view)
if markdown_view.buffer_id() == 0:
return

total_region = sublime.Region(0, markdown_view.size())
markdown = markdown_view.substr(total_region)

html = self.markdowner.convert(markdown)
print(html)

# FIXME: replace images
basepath = os.path.dirname(markdown_view.file_name())
html = markdown2html(markdown, basepath, partial(self._update_preview,
markdown_view))

self.phantom_sets[markdown_view.id()].update([
sublime.Phantom(sublime.Region(0), html, sublime.LAYOUT_BLOCK,
lambda href: sublime.run_command('open_url', {'url': href}))
])


7 changes: 7 additions & 0 deletions dependencies.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"*": {
"*": [
"bs4"
]
}
}
Binary file added live-testing/sublime_text.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions live-testing/test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# hello world

This is a *test*.

I'm not sure that it **actually** going to work, but it seems nicer than the [previous version][prev]

This is the first image from the local file system (absolute path, sorry):

![The sublime text logo!](file:///home/math2001/.config/sublime-text-3/Packages/MarkdownLivePreview2/live-testing/sublime_text.png)

This is the first image from the local file system, *relative* path!

![The sublime text logo!](sublime_text.png)

This is the first image from the internet!


![The sublime text logo!](https://www.sublimehq.com/images/sublime_text.png)

[prev]: https://github.com/math2001/MarkdownLivePreview/tree/d4c477749ce7e77b8e9fc85464a2488f003c45bc
69 changes: 69 additions & 0 deletions markdown2html.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import base64
import os.path
from functools import lru_cache
from .lib.markdown2 import Markdown
from bs4 import BeautifulSoup

__all__ = ('markdown2html', )

markdowner = Markdown()

# FIXME: put a nice picture please :^)
BASE64_LOADING_IMAGE = 'loading image!'
BASE64_404_IMAGE = '404 not found :-('

class LoadingError(Exception):
pass

def markdown2html(markdown, basepath, re_render):
""" converts the markdown to html, loads the images and puts in base64 for sublime
to understand them correctly. That means that we are responsible for loading the
images from the internet. Hence, we take in re_render, which is just a function we
call when an image has finished loading to retrigger a render (see #90)
"""
html = markdowner.convert(markdown)

soup = BeautifulSoup(html, "html.parser")
for img_element in soup.find_all('img'):
src = img_element['src']
# already in base64, or something of the like
# FIXME: what other types are possible? Are they handled by ST? If not, could we
# convert it into base64? is it worth the effort?
if src.startswith('data:image/'):
continue

if src.startswith('http://') or src.startswith('https://'):
path = src
elif src.startswith('file://'):
path = src[len('file://'):]
else:
# expanduser: ~ -> /home/math2001
# realpath: simplify that paths so that we don't have duplicated caches
path = os.path.realpath(os.path.expanduser(os.path.join(basepath, src)))

try:
base64 = get_base64_image(path)
except FileNotFoundError as e:
print("{!r} not found {!r}".format(path, e))
base64 = BASE64_404_IMAGE
except LoadingError:
# the image is loading
base64 = BASE64_LOADING_IMAGE

img_element['src'] = base64

# FIXME: how do tables look? should we use ascii tables?

return str(soup)

# FIXME: This is an in memory cache. 20 seems like a fair bit of images... Should it be
# bigger? Should the user be allowed to chose? There definitely should be a limit
# because we don't wanna use to much memory, we're a simple markdown preview plugin
@lru_cache(maxsize=20)
def get_base64_image(path):
if path.startswith('http://') or path.startswith('https://'):
return 'loading of the internet!'

with open(path, 'rb') as fp:
return 'data:image/png;base64,' + base64.b64encode(fp.read()).decode('utf-8')

8 changes: 0 additions & 8 deletions test.md

This file was deleted.

0 comments on commit bae26fc

Please sign in to comment.