def run(self, view, wholefile=False, preview=False): ''' return full html and body html for view. ''' self.settings = Settings('MarkdownPreview.sublime-settings', view.file_name()) self.preview = preview self.view = view contents = self.get_contents(wholefile) body = self.convert_markdown(contents) html_template = self.settings.get('html_template') # use customized html template if given if self.settings.get('html_simple', False): html = body elif html_template and os.path.exists(html_template): head = u'' head += self.get_meta() if not self.settings.get('skip_default_stylesheet'): head += self.get_stylesheet() head += self.get_javascript() head += self.get_highlight() head += self.get_mathjax() head += self.get_uml() head += self.get_title() html = load_utf8(html_template) html = html.replace('{{ HEAD }}', head, 1) html = html.replace('{{ BODY }}', body, 1) else: html = u'<!DOCTYPE html>' html += '<html><head><meta charset="utf-8">' html += '<html><head><meta name="viewport" content="width=device-width,initial-scale=1.0"/>' html += self.get_meta() html += self.get_stylesheet() html += self.get_javascript() html += self.get_highlight() html += self.get_mathjax() html += self.get_uml() html += self.get_title() html += '</head><body>' html += '<article class="markdown-body">' html += body html += '</article>' html += '</body>' html += '</html>' return html, body
def run(self): ''' return full html and body html for view. ''' self.settings = Settings('MarkdownPreview.sublime-settings', self.filename) contents = self.get_contents(self.filename) body = self.convert_markdown(contents) # add self.html_template, it can be assigned. if self.html_template is None: html_template = self.settings.get('html_template') else: html_template = self.html_template # use customized html template if given if self.settings.get('html_simple', False): html = body elif html_template and os.path.exists(html_template): head = u'' head += self.get_meta() if not self.settings.get('skip_default_stylesheet'): head += self.get_stylesheet() head += self.get_javascript() head += self.get_highlight() head += self.get_mathjax() head += self.get_uml() head += self.get_title() html = load_utf8(html_template) html = html.replace('{{ HEAD }}', head, 1) html = html.replace('{{ BODY }}', body, 1) else: html = u'<!DOCTYPE html>' html += '<html><head><meta charset="utf-8">' html += self.get_meta() html += self.get_stylesheet() html += self.get_javascript() html += self.get_highlight() html += self.get_mathjax() html += self.get_title() html += '</head><body>' html += body html += '</body>' html += '</html>' return html, body
def run(self, view, wholefile=False, preview=False): """ return full html and body html for view. """ self.settings = Settings("MarkdownPreview.sublime-settings", view.file_name()) self.preview = preview self.view = view contents = self.get_contents(wholefile) body = self.convert_markdown(contents) html_template = self.settings.get("html_template") # use customized html template if given if self.settings.get("html_simple", False): html = body elif html_template and os.path.exists(html_template): head = u"" head += self.get_meta() if not self.settings.get("skip_default_stylesheet"): head += self.get_stylesheet() head += self.get_javascript() head += self.get_highlight() head += self.get_mathjax() head += self.get_uml() head += self.get_title() html = load_utf8(html_template) html = html.replace("{{ HEAD }}", head, 1) html = html.replace("{{ BODY }}", body, 1) else: html = u"<!DOCTYPE html>" html += '<html><head><meta charset="utf-8">' html += self.get_meta() html += self.get_stylesheet() html += self.get_javascript() html += self.get_highlight() html += self.get_mathjax() html += self.get_uml() html += self.get_title() html += "</head><body>" html += '<article class="markdown-body">' html += body html += "</article>" html += "</body>" html += "</html>" return html, body
class Compiler(object): """ Do the markdown converting """ default_css = "markdown.css" def isurl(self, css_name): match = re.match(r"https?://", css_name) if match: return True return False def get_font_css(self): fonts = "" css_names = ["fonts/migu-1c-regular.css"] for css_name in css_names: fonts = u"%s%s" % (fonts, load_resource(css_name)) return u"<style>%s</style>" % fonts def get_default_css(self): """ locate the correct CSS with the 'css' setting """ css_name = self.settings.get("css", "default") if self.isurl(css_name): # link to remote URL return u"<link href='%s' rel='stylesheet' type='text/css'>" % css_name elif os.path.isfile(os.path.expanduser(css_name)): # use custom CSS file return u"<style>%s</style>" % load_utf8(os.path.expanduser(css_name)) elif css_name == "default": # use parser CSS file return u"<style>%s</style>" % load_resource(self.default_css) return "" def get_override_css(self): """ handls allow_css_overrides setting. """ if self.settings.get("allow_css_overrides"): filename = self.view.file_name() filetypes = self.settings.get("markdown_filetypes") if filename and filetypes: for filetype in filetypes: if filename.endswith(filetype): css_filename = filename.rpartition(filetype)[0] + ".css" if os.path.isfile(css_filename): return u"<style>%s</style>" % load_utf8(css_filename) return "" def get_stylesheet(self): """ return the correct CSS file based on parser and settings """ return self.get_font_css() + self.get_default_css() + self.get_override_css() def get_javascript(self): js_files = self.settings.get("js") scripts = "" if js_files is not None: # Ensure string values become a list. if isinstance(js_files, str) or isinstance(js_files, unicode_str): js_files = [js_files] # Only load scripts if we have a list. if isinstance(js_files, list): for js_file in js_files: if os.path.isabs(js_file): # Load the script inline to avoid cross-origin. scripts += u"<script>%s</script>" % load_utf8(js_file) else: scripts += u"<script type='text/javascript' src='%s'></script>" % js_file return scripts def get_mathjax(self): """ return the MathJax script if enabled """ if self.settings.get("enable_mathjax") is True: return load_resource("mathjax.html") return "" def get_uml(self): """ return the uml scripts if enabled """ if self.settings.get("enable_uml") is True: flow = load_resource("flowchart-min.js") return load_resource("uml.html").replace("{{ flowchart }}", flow, 1) return "" def get_highlight(self): return "" def get_contents(self, wholefile=False): """ Get contents or selection from view and optionally strip the YAML front matter """ region = sublime.Region(0, self.view.size()) contents = self.view.substr(region) if not wholefile: # use selection if any selection = self.view.substr(self.view.sel()[0]) if selection.strip() != "": contents = selection # Remove yaml front matter if self.settings.get("strip_yaml_front_matter") and contents.startswith("---"): frontmatter, contents = self.preprocessor_yaml_frontmatter(contents) self.settings.apply_frontmatter(frontmatter) references = self.settings.get("builtin").get("references", []) for ref in references: contents += get_references(ref) contents = self.parser_specific_preprocess(contents) return contents def parser_specific_preprocess(self, text): return text def preprocessor_yaml_frontmatter(self, text): """ Get frontmatter from string """ frontmatter = {} if text.startswith("---"): m = re.search(r"^(---(.*?)---[ \t]*\r?\n)", text, re.DOTALL) if m: try: frontmatter = yaml.load(m.group(2)) except: print(traceback.format_exc()) text = text[m.end(1) :] return frontmatter, text def parser_specific_postprocess(self, text): return text def postprocessor_pathconverter(self, html, image_convert, file_convert, absolute=False): RE_TAG_HTML = r"""(?xus) (?: (?P<comments>(\r?\n?\s*)<!--[\s\S]*?-->(\s*)(?=\r?\n)|<!--[\s\S]*?-->)| (?P<open><(?P<tag>(?:%s))) (?P<attr>(?:\s+[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*) (?P<close>\s*(?:\/?)>) ) """ RE_TAG_LINK_ATTR = re.compile( r"""(?xus) (?P<attr> (?: (?P<name>\s+(?:href|src)\s*=\s*) (?P<path>"[^"]*"|'[^']*') ) ) """ ) RE_SOURCES = re.compile( RE_TAG_HTML % ( (r"img" if image_convert else "") + (r"|" if image_convert and file_convert else "") + (r"script|a|link" if file_convert else "") ) ) def repl(m, base_path, rel_path=None): if m.group("comments"): tag = m.group("comments") else: tag = m.group("open") if rel_path is None: tag += RE_TAG_LINK_ATTR.sub(lambda m2: repl_absolute(m2, base_path), m.group("attr")) else: tag += RE_TAG_LINK_ATTR.sub(lambda m2: repl_relative(m2, base_path, rel_path), m.group("attr")) tag += m.group("close") return tag basepath = self.settings.get("builtin").get("basepath") if basepath is None: basepath = "" if absolute: if basepath: return RE_SOURCES.sub(lambda m: repl(m, basepath), html) else: if self.preview: relativepath = getTempMarkdownPreviewPath(self.view) else: relativepath = self.settings.get("builtin").get("destination") if not relativepath: mdfile = self.view.file_name() if mdfile is not None and os.path.exists(mdfile): relativepath = os.path.splitext(mdfile)[0] + ".html" if relativepath: relativepath = os.path.dirname(relativepath) if basepath and relativepath: return RE_SOURCES.sub(lambda m: repl(m, basepath, relativepath), html) return html def postprocessor_base64(self, html): """ convert resources (currently images only) to base64 """ file_types = {(".png",): "image/png", (".jpg", ".jpeg"): "image/jpeg", (".gif",): "image/gif"} exclusion_list = tuple(["https://", "http://", "#"] + ["data:%s;base64," % ft for ft in file_types.values()]) RE_WIN_DRIVE = re.compile(r"(^[A-Za-z]{1}:(?:\\|/))") RE_TAG_HTML = re.compile( r"""(?xus) (?: (?P<comments>(\r?\n?\s*)<!--[\s\S]*?-->(\s*)(?=\r?\n)|<!--[\s\S]*?-->)| (?P<open><(?P<tag>img)) (?P<attr>(?:\s+[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*) (?P<close>\s*(?:\/?)>) ) """ ) RE_TAG_LINK_ATTR = re.compile( r"""(?xus) (?P<attr> (?: (?P<name>\s+src\s*=\s*) (?P<path>"[^"]*"|'[^']*') ) ) """ ) def b64(m): import base64 src = url2pathname(m.group("path")[1:-1]) data = m.group(0) base_path = self.settings.get("builtin").get("basepath") if base_path is None: base_path = "" # Format the link absolute = False if src.startswith("file://"): src = src.replace("file://", "", 1) if sublime.platform() == "windows" and not src.startswith("//"): src = src.lstrip("/") absolute = True elif sublime.platform() == "windows" and RE_WIN_DRIVE.match(src) is not None: absolute = True # Make sure we are working with an absolute path if not src.startswith(exclusion_list): if absolute: src = os.path.normpath(src) else: src = os.path.normpath(os.path.join(base_path, src)) if os.path.exists(src): ext = os.path.splitext(src)[1].lower() for b64_ext in file_types: if ext in b64_ext: try: with open(src, "rb") as f: data = ' src="data:%s;base64,%s"' % ( file_types[b64_ext], base64.b64encode(f.read()).decode("ascii"), ) except Exception: pass break return data def repl(m): if m.group("comments"): tag = m.group("comments") else: tag = m.group("open") tag += RE_TAG_LINK_ATTR.sub(lambda m2: b64(m2), m.group("attr")) tag += m.group("close") return tag return RE_TAG_HTML.sub(repl, html) def postprocessor_simple(self, html): """ Strip out ids and classes for a simplified HTML output """ def repl(m): if m.group("comments"): tag = "" else: tag = m.group("open") tag += RE_TAG_BAD_ATTR.sub("", m.group("attr")) tag += m.group("close") return tag # Strip out id, class, on<word>, and style attributes for a simple html output RE_TAG_HTML = re.compile( r"""(?x) (?: (?P<comments>(\r?\n?\s*)<!--[\s\S]*?-->(\s*)(?=\r?\n)|<!--[\s\S]*?-->)| (?P<open><[\w\:\.\-]+) (?P<attr>(?:\s+[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*) (?P<close>\s*(?:\/?)>) ) """, re.DOTALL | re.UNICODE, ) RE_TAG_BAD_ATTR = re.compile( r"""(?x) (?P<attr> (?: \s+(?:id|class|style|on[\w]+) (?:\s*=\s*(?:"[^"]*"|'[^']*')) )* ) """, re.DOTALL | re.UNICODE, ) return RE_TAG_HTML.sub(repl, html) def convert_markdown(self, markdown_text): """ convert input markdown to HTML, with github or builtin parser """ markdown_html = self.parser_specific_convert(markdown_text) image_convert = self.settings.get("image_path_conversion", "absolute") file_convert = self.settings.get("file_path_conversions", "absolute") markdown_html = self.parser_specific_postprocess(markdown_html) if "absolute" in (image_convert, file_convert): markdown_html = self.postprocessor_pathconverter(markdown_html, image_convert, file_convert, True) if "relative" in (image_convert, file_convert): markdown_html = self.postprocessor_pathconverter(markdown_html, image_convert, file_convert, False) if image_convert == "base64": markdown_html = self.postprocessor_base64(markdown_html) if self.settings.get("html_simple", False): markdown_html = self.postprocessor_simple(markdown_html) return markdown_html def get_title(self): if self.meta_title is not None: title = self.meta_title else: title = self.view.name() if not title: fn = self.view.file_name() title = "untitled" if not fn else os.path.splitext(os.path.basename(fn))[0] return "<title>%s</title>" % cgi.escape(title) def get_meta(self): self.meta_title = None meta = [] for k, v in self.settings.get("meta", {}).items(): if k == "title": if isinstance(v, list): if len(v) == 0: v = "" else: v = v[0] self.meta_title = unicode_str(v) continue if isinstance(v, list): v = ",".join(v) if v is not None: meta.append('<meta name="%s" content="%s">' % (cgi.escape(k, True), cgi.escape(v, True))) return "\n".join(meta) def run(self, view, wholefile=False, preview=False): """ return full html and body html for view. """ self.settings = Settings("MarkdownPreview.sublime-settings", view.file_name()) self.preview = preview self.view = view contents = self.get_contents(wholefile) body = self.convert_markdown(contents) html_template = self.settings.get("html_template") # use customized html template if given if self.settings.get("html_simple", False): html = body elif html_template and os.path.exists(html_template): head = u"" head += self.get_meta() if not self.settings.get("skip_default_stylesheet"): head += self.get_stylesheet() head += self.get_javascript() head += self.get_highlight() head += self.get_mathjax() head += self.get_uml() head += self.get_title() html = load_utf8(html_template) html = html.replace("{{ HEAD }}", head, 1) html = html.replace("{{ BODY }}", body, 1) else: html = u"<!DOCTYPE html>" html += '<html><head><meta charset="utf-8">' html += self.get_meta() html += self.get_stylesheet() html += self.get_javascript() html += self.get_highlight() html += self.get_mathjax() html += self.get_uml() html += self.get_title() html += "</head><body>" html += '<article class="markdown-body">' html += body html += "</article>" html += "</body>" html += "</html>" return html, body
class Compiler(object): ''' Do the markdown converting ''' default_css = "markdown.css" filename = "../first.md" html_template = None def __init__(self, file_name, html_template=None): self.filename = file_name def isurl(self, css_name): match = re.match(r'https?://', css_name) if match: return True return False def get_default_css(self): ''' locate the correct CSS with the 'css' setting ''' css_name = self.settings.get('css', 'default') if self.isurl(css_name): # link to remote URL return u"<link href='%s' rel='stylesheet' type='text/css'>" % css_name elif os.path.isfile(os.path.expanduser(css_name)): # use custom CSS file return u"<style>%s</style>" % load_utf8( os.path.expanduser(css_name)) elif css_name == 'default': # use parser CSS file return u"<style>%s</style>" % load_resource(self.default_css) return '' def get_override_css(self): ''' handls allow_css_overrides setting. ''' if self.settings.get('allow_css_overrides'): filetypes = self.settings.get('markdown_filetypes') if self.filename and filetypes: for filetype in filetypes: if self.filename.endswith(filetype): css_filename = self.filename.rpartition( filetype)[0] + '.css' if (os.path.isfile(css_filename)): return u"<style>%s</style>" % load_utf8( css_filename) return '' def get_stylesheet(self): ''' return the correct CSS file based on parser and settings ''' return self.get_default_css() + self.get_override_css() def get_javascript(self): js_files = self.settings.get('js') scripts = '' if js_files is not None: # Ensure string values become a list. if isinstance(js_files, str) or isinstance(js_files, unicode_str): js_files = [js_files] # Only load scripts if we have a list. if isinstance(js_files, list): for js_file in js_files: if os.path.isabs(js_file): # Load the script inline to avoid cross-origin. scripts += u"<script>%s</script>" % load_utf8(js_file) else: scripts += u"<script type='text/javascript' src='%s'></script>" % js_file return scripts def get_mathjax(self): ''' return the MathJax script if enabled ''' if self.settings.get('enable_mathjax') is True: return load_resource('mathjax.html') return '' def get_uml(self): ''' return the uml scripts if enabled ''' if self.settings.get('enable_uml') is True: flow = load_resource('flowchart-min.js') return load_resource('uml.html').replace('{{ flowchart }}', flow, 1) return '' def get_highlight(self): return '' def get_contents(self, filename): ''' Get contents or selection from view and optionally strip the YAML front matter ''' # TODO: 获取文件内容,从这里开始 contents = load_resource(filename) # Remove yaml front matter if self.settings.get( 'strip_yaml_front_matter') and contents.startswith('---'): frontmatter, contents = self.preprocessor_yaml_frontmatter( contents) self.settings.apply_frontmatter(frontmatter) references = self.settings.get('builtin').get('references', []) for ref in references: contents += get_references(ref) contents = self.parser_specific_preprocess(contents) return contents def parser_specific_preprocess(self, text): return text def preprocessor_yaml_frontmatter(self, text): """ Get frontmatter from string """ frontmatter = {} if text.startswith("---"): m = re.search(r'^(---(.*?)---\r?\n)', text, re.DOTALL) if m: try: frontmatter = yaml.load(m.group(2)) except: print(traceback.format_exc()) text = text[m.end(1):] return frontmatter, text def parser_specific_postprocess(self, text): return text def postprocessor_absolute(self, html, image_convert, file_convert): ''' fix relative paths in images, scripts, and links for the internal parser ''' def tag_fix(m): tag = m.group('tag') src = m.group('src') if self.filename: # TODO: delete paltform = windows if (not src.startswith(ABS_EXCLUDE) and not (RE_WIN_DRIVE.match(src) is not None)): # Don't explicitly add file:// prefix, # But don't remove them either abs_path = u'%s/%s' % (os.path.dirname(self.filename), src) # Don't replace just the first instance, # but explicitly place it where it was before # to ensure a file name 'img' dosen't replace # the tag name etc. if os.path.exists(abs_path): tag = m.group('begin') + abs_path + m.group('end') return tag # Compile the appropriate regex to find images and/or files RE_WIN_DRIVE = re.compile(r"(^[A-Za-z]{1}:(?:\\|/))") RE_SOURCES = re.compile( r"""(?P<tag>(?P<begin><(?:%s%s%s)[^>]+(?:src%s)=["'])(?P<src>[^"']+)(?P<end>[^>]*>))""" % (r"img" if image_convert else "", r"|" if image_convert and file_convert else "", r"script|a" if file_convert else "", r"|href" if file_convert else "")) return RE_SOURCES.sub(tag_fix, html) def postprocessor_pathconverter(self, html, image_convert, file_convert, absolute=False): RE_TAG_HTML = r'''(?xus) (?: (?P<comments>(\r?\n?\s*)<!--[\s\S]*?-->(\s*)(?=\r?\n)|<!--[\s\S]*?-->)| (?P<open><(?P<tag>(?:%s))) (?P<attr>(?:\s+[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*) (?P<close>\s*(?:\/?)>) ) ''' RE_TAG_LINK_ATTR = re.compile(r'''(?xus) (?P<attr> (?: (?P<name>\s+(?:href|src)\s*=\s*) (?P<path>"[^"]*"|'[^']*') ) ) ''') RE_SOURCES = re.compile( RE_TAG_HTML % ((r"img" if image_convert else "") + (r"|" if image_convert and file_convert else "") + (r"script|a|link" if file_convert else ""))) def repl(m, base_path, rel_path=None): if m.group('comments'): tag = m.group('comments') else: tag = m.group('open') if rel_path is None: tag += RE_TAG_LINK_ATTR.sub( lambda m2: repl_absolute(m2, base_path), m.group('attr')) else: tag += RE_TAG_LINK_ATTR.sub( lambda m2: repl_relative(m2, base_path, rel_path), m.group('attr')) tag += m.group('close') return tag basepath = self.settings.get('builtin').get("basepath") if basepath is None: basepath = "" if absolute: if basepath: return RE_SOURCES.sub(lambda m: repl(m, basepath), html) else: if self.preview: relativepath = getTempMarkdownPreviewPath(self.view) else: relativepath = self.settings.get('builtin').get("destination") if not relativepath: mdfile = self.view.file_name() if mdfile is not None and os.path.exists(mdfile): relativepath = os.path.splitext(mdfile)[0] + '.html' if relativepath: relativepath = os.path.dirname(relativepath) if basepath and relativepath: return RE_SOURCES.sub( lambda m: repl(m, basepath, relativepath), html) return html def postprocessor_base64(self, html): ''' convert resources (currently images only) to base64 ''' file_types = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif" } exclusion_list = tuple( ['https://', 'http://', '#'] + ["data:%s;base64," % ft for ft in file_types.values()]) def b64(m): import base64 src = m.group('src') data = m.group('tag') base_path = self.settings.get("basepath") if base_path is None: base_path = "" # Format the link absolute = False if src.startswith('file://'): src = src.replace('file://', '', 1) # if sublime.platform() == "windows" and not src.startswith('//'): # src = src.lstrip("/") absolute = True # elif sublime.platform() == "windows" and RE_WIN_DRIVE.match(src) is not None: # absolute = True # Make sure we are working with an absolute path if not src.startswith(exclusion_list): if absolute: src = os.path.normpath(src) else: src = os.path.normpath(os.path.join(base_path, src)) if os.path.exists(src): ext = os.path.splitext(src)[1].lower() if ext in file_types: try: with open(src, "rb") as f: data = m.group( 'begin') + "data:%s;base64,%s" % ( file_types[ext], base64.b64encode(f.read()).decode( 'ascii')) + m.group('end') except Exception: pass return data RE_WIN_DRIVE = re.compile(r"(^[A-Za-z]{1}:(?:\\|/))") RE_SOURCES = re.compile( r"""(?P<tag>(?P<begin><(?:img)[^>]+(?:src)=["'])(?P<src>[^"']+)(?P<end>[^>]*>))""" ) return RE_SOURCES.sub(b64, html) def postprocessor_simple(self, html): ''' Strip out ids and classes for a simplified HTML output ''' def strip_html(m): tag = m.group('open') if m.group('attr1'): tag += m.group('attr1') if m.group('attr2'): tag += m.group('attr2') if m.group('attr3'): tag += m.group('attr3') if m.group('attr4'): tag += m.group('attr4') if m.group('attr5'): tag += m.group('attr5') if m.group('attr6'): tag += m.group('attr6') tag += m.group('close') return tag # Strip out id, class and style attributes for a simple html output # Since we are stripping out two attributes, we need to set up the groups in such # a way so we can retrieve the data we don't want to throw away # up to these worst case scenarios: # # <tag attr=""... (id|class|style)=""... attr=""... (id|class|style)=""... attr=""... (id|class|style)=""...> # <tag (id|class|style)=""... attr=""... (id|class|style)=""... attr=""... (id|class|style)=""... attr=""...> STRIP_HTML = re.compile( r''' (?P<open><[\w\:\.\-]+) # Tag open (?: (?P<attr1>(?:\s+(?!id|class|style)[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*) # Attributes to keep | (?P<target1>\s+(?:id|class|style)(?:\s*=\s*(?:"[^"]*"|'[^']*'))*) # Attributes to delte ) (?: (?P<attr2>(?:\s+(?!id|class|style)[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*) # Attributes to keep | (?P<target2>\s+(?:id|class|style)(?:\s*=\s*(?:"[^"]*"|'[^']*'))*) # Attributes to delte )? (?: (?P<attr3>(?:\s+(?!id|class|style)[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*) # Attributes to keep | (?P<target3>\s+(?:id|class|style)(?:\s*=\s*(?:"[^"]*"|'[^']*'))*) # Attributes to delte )? (?: (?P<attr4>(?:\s+(?!id|class|style)[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*) # Attributes to keep | (?P<target4>\s+(?:id|class|style)(?:\s*=\s*(?:"[^"]*"|'[^']*'))*) # Attributes to delte )? (?: (?P<attr5>(?:\s+(?!id|class|style)[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*) # Attributes to keep | (?P<target5>\s+(?:id|class|style)(?:\s*=\s*(?:"[^"]*"|'[^']*'))*) # Attributes to delte )? (?: (?P<attr6>(?:\s+(?!id|class|style)[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*) # Attributes to keep | (?P<target6>\s+(?:id|class|style)(?:\s*=\s*(?:"[^"]*"|'[^']*'))*) # Attributes to delte )? (?P<close>\s*(?:\/?)>) # Tag end ''', re.MULTILINE | re.DOTALL | re.VERBOSE) return STRIP_HTML.sub(strip_html, html) def convert_markdown(self, markdown_text): ''' convert input markdown to HTML, with github or builtin parser ''' markdown_html = self.parser_specific_convert(markdown_text) image_convert = self.settings.get("image_path_conversion", "absolute") file_convert = self.settings.get("file_path_conversions", "absolute") markdown_html = self.parser_specific_postprocess(markdown_html) if "absolute" in (image_convert, file_convert): markdown_html = self.postprocessor_pathconverter( markdown_html, image_convert, file_convert, True) if "relative" in (image_convert, file_convert): markdown_html = self.postprocessor_pathconverter( markdown_html, image_convert, file_convert, False) if image_convert == "base64": markdown_html = self.postprocessor_base64(markdown_html) if self.settings.get("html_simple", False): markdown_html = self.postprocessor_simple(markdown_html) return markdown_html def get_title(self): if self.meta_title is not None: title = self.meta_title else: title = self.filename # TODO: I changed here if not title: fn = self.filename title = 'untitled' if not fn else os.path.splitext( os.path.basename(fn))[0] return '<title>%s</title>' % cgi.escape(title) def get_meta(self): self.meta_title = None meta = [] for k, v in self.settings.get("meta", {}).items(): if k == "title": if isinstance(v, list): if len(v) == 0: v = "" else: v = v[0] self.meta_title = unicode_str(v) continue if isinstance(v, list): v = ','.join(v) if v is not None: meta.append('<meta name="%s" content="%s">' % (cgi.escape(k, True), cgi.escape(v, True))) return '\n'.join(meta) # 主程序入口 def run(self): ''' return full html and body html for view. ''' self.settings = Settings('MarkdownPreview.sublime-settings', self.filename) contents = self.get_contents(self.filename) body = self.convert_markdown(contents) # add self.html_template, it can be assigned. if self.html_template is None: html_template = self.settings.get('html_template') else: html_template = self.html_template # use customized html template if given if self.settings.get('html_simple', False): html = body elif html_template and os.path.exists(html_template): head = u'' head += self.get_meta() if not self.settings.get('skip_default_stylesheet'): head += self.get_stylesheet() head += self.get_javascript() head += self.get_highlight() head += self.get_mathjax() head += self.get_uml() head += self.get_title() html = load_utf8(html_template) html = html.replace('{{ HEAD }}', head, 1) html = html.replace('{{ BODY }}', body, 1) else: html = u'<!DOCTYPE html>' html += '<html><head><meta charset="utf-8">' html += self.get_meta() html += self.get_stylesheet() html += self.get_javascript() html += self.get_highlight() html += self.get_mathjax() html += self.get_title() html += '</head><body>' html += body html += '</body>' html += '</html>' return html, body
class Compiler(object): ''' Do the markdown converting ''' default_css = "markdown.css" def isurl(self, css_name): match = re.match(r'https?://', css_name) if match: return True return False def get_default_css(self): ''' locate the correct CSS with the 'css' setting ''' css_name = self.settings.get('css', 'default') if self.isurl(css_name): # link to remote URL return u"<link href='%s' rel='stylesheet' type='text/css'>" % css_name elif os.path.isfile(os.path.expanduser(css_name)): # use custom CSS file return u"<style>%s</style>" % load_utf8( os.path.expanduser(css_name)) elif css_name == 'default': # use parser CSS file return u"<style>%s</style>" % load_resource(self.default_css) return '' def get_override_css(self): ''' handls allow_css_overrides setting. ''' if self.settings.get('allow_css_overrides'): filename = self.view.file_name() filetypes = self.settings.get('markdown_filetypes') if filename and filetypes: for filetype in filetypes: if filename.endswith(filetype): css_filename = filename.rpartition( filetype)[0] + '.css' if (os.path.isfile(css_filename)): return u"<style>%s</style>" % load_utf8( css_filename) return '' def get_stylesheet(self): ''' return the correct CSS file based on parser and settings ''' return self.get_default_css() + self.get_override_css() def get_javascript(self): js_files = self.settings.get('js') scripts = '' if js_files is not None: # Ensure string values become a list. if isinstance(js_files, str) or isinstance(js_files, unicode_str): js_files = [js_files] # Only load scripts if we have a list. if isinstance(js_files, list): for js_file in js_files: if os.path.isabs(js_file): # Load the script inline to avoid cross-origin. scripts += u"<script>%s</script>" % load_utf8(js_file) else: scripts += u"<script type='text/javascript' src='%s'></script>" % js_file return scripts def get_mathjax(self): ''' return the MathJax script if enabled ''' if self.settings.get('enable_mathjax') is True: return load_resource('mathjax.html') return '' def get_uml(self): ''' return the uml scripts if enabled ''' if self.settings.get('enable_uml') is True: flow = load_resource('flowchart-min.js') return load_resource('uml.html').replace('{{ flowchart }}', flow, 1) return '' def get_highlight(self): return '' def get_contents(self, wholefile=False): ''' Get contents or selection from view and optionally strip the YAML front matter ''' region = sublime.Region(0, self.view.size()) contents = self.view.substr(region) if not wholefile: # use selection if any selection = self.view.substr(self.view.sel()[0]) if selection.strip() != '': contents = selection # Remove yaml front matter if self.settings.get( 'strip_yaml_front_matter') and contents.startswith('---'): frontmatter, contents = self.preprocessor_yaml_frontmatter( contents) self.settings.apply_frontmatter(frontmatter) references = self.settings.get('builtin').get('references', []) for ref in references: contents += get_references(ref) contents = self.parser_specific_preprocess(contents) return contents def parser_specific_preprocess(self, text): return text def preprocessor_yaml_frontmatter(self, text): """ Get frontmatter from string """ frontmatter = {} if text.startswith("---"): m = re.search(r'^(---(.*?)---[ \t]*\r?\n)', text, re.DOTALL) if m: try: frontmatter = yaml.load(m.group(2)) except: print(traceback.format_exc()) text = text[m.end(1):] return frontmatter, text def parser_specific_postprocess(self, text): return text def postprocessor_pathconverter(self, html, image_convert, file_convert, absolute=False): RE_TAG_HTML = r'''(?xus) (?: (?P<comments>(\r?\n?\s*)<!--[\s\S]*?-->(\s*)(?=\r?\n)|<!--[\s\S]*?-->)| (?P<open><(?P<tag>(?:%s))) (?P<attr>(?:\s+[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*) (?P<close>\s*(?:\/?)>) ) ''' RE_TAG_LINK_ATTR = re.compile(r'''(?xus) (?P<attr> (?: (?P<name>\s+(?:href|src)\s*=\s*) (?P<path>"[^"]*"|'[^']*') ) ) ''') RE_SOURCES = re.compile( RE_TAG_HTML % ((r"img" if image_convert else "") + (r"|" if image_convert and file_convert else "") + (r"script|a|link" if file_convert else ""))) def repl(m, base_path, rel_path=None): if m.group('comments'): tag = m.group('comments') else: tag = m.group('open') if rel_path is None: tag += RE_TAG_LINK_ATTR.sub( lambda m2: repl_absolute(m2, base_path), m.group('attr')) else: tag += RE_TAG_LINK_ATTR.sub( lambda m2: repl_relative(m2, base_path, rel_path), m.group('attr')) tag += m.group('close') return tag basepath = self.settings.get('builtin').get("basepath") if basepath is None: basepath = "" if absolute: if basepath: return RE_SOURCES.sub(lambda m: repl(m, basepath), html) else: if self.preview: relativepath = getTempMarkdownPreviewPath(self.view) else: relativepath = self.settings.get('builtin').get("destination") if not relativepath: mdfile = self.view.file_name() if mdfile is not None and os.path.exists(mdfile): relativepath = os.path.splitext(mdfile)[0] + '.html' if relativepath: relativepath = os.path.dirname(relativepath) if basepath and relativepath: return RE_SOURCES.sub( lambda m: repl(m, basepath, relativepath), html) return html def postprocessor_base64(self, html): ''' convert resources (currently images only) to base64 ''' file_types = { (".png", ): "image/png", (".jpg", ".jpeg"): "image/jpeg", (".gif", ): "image/gif" } exclusion_list = tuple( ['https://', 'http://', '#'] + ["data:%s;base64," % ft for ft in file_types.values()]) RE_WIN_DRIVE = re.compile(r"(^[A-Za-z]{1}:(?:\\|/))") RE_TAG_HTML = re.compile(r'''(?xus) (?: (?P<comments>(\r?\n?\s*)<!--[\s\S]*?-->(\s*)(?=\r?\n)|<!--[\s\S]*?-->)| (?P<open><(?P<tag>img)) (?P<attr>(?:\s+[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*) (?P<close>\s*(?:\/?)>) ) ''') RE_TAG_LINK_ATTR = re.compile(r'''(?xus) (?P<attr> (?: (?P<name>\s+src\s*=\s*) (?P<path>"[^"]*"|'[^']*') ) ) ''') def b64(m): import base64 src = url2pathname(m.group('path')[1:-1]) data = m.group(0) base_path = self.settings.get('builtin').get("basepath") if base_path is None: base_path = "" # Format the link absolute = False if src.startswith('file://'): src = src.replace('file://', '', 1) if sublime.platform( ) == "windows" and not src.startswith('//'): src = src.lstrip("/") absolute = True elif sublime.platform() == "windows" and RE_WIN_DRIVE.match( src) is not None: absolute = True # Make sure we are working with an absolute path if not src.startswith(exclusion_list): if absolute: src = os.path.normpath(src) else: src = os.path.normpath(os.path.join(base_path, src)) if os.path.exists(src): ext = os.path.splitext(src)[1].lower() for b64_ext in file_types: if ext in b64_ext: try: with open(src, "rb") as f: data = " src=\"data:%s;base64,%s\"" % ( file_types[b64_ext], base64.b64encode( f.read()).decode('ascii')) except Exception: pass break return data def repl(m): if m.group('comments'): tag = m.group('comments') else: tag = m.group('open') tag += RE_TAG_LINK_ATTR.sub(lambda m2: b64(m2), m.group('attr')) tag += m.group('close') return tag return RE_TAG_HTML.sub(repl, html) def postprocessor_simple(self, html): ''' Strip out ids and classes for a simplified HTML output ''' def repl(m): if m.group('comments'): tag = '' else: tag = m.group('open') tag += RE_TAG_BAD_ATTR.sub('', m.group('attr')) tag += m.group('close') return tag # Strip out id, class, on<word>, and style attributes for a simple html output RE_TAG_HTML = re.compile( r'''(?x) (?: (?P<comments>(\r?\n?\s*)<!--[\s\S]*?-->(\s*)(?=\r?\n)|<!--[\s\S]*?-->)| (?P<open><[\w\:\.\-]+) (?P<attr>(?:\s+[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*) (?P<close>\s*(?:\/?)>) ) ''', re.DOTALL | re.UNICODE) RE_TAG_BAD_ATTR = re.compile( r'''(?x) (?P<attr> (?: \s+(?:id|class|style|on[\w]+) (?:\s*=\s*(?:"[^"]*"|'[^']*')) )* ) ''', re.DOTALL | re.UNICODE) return RE_TAG_HTML.sub(repl, html) def convert_markdown(self, markdown_text): ''' convert input markdown to HTML, with github or builtin parser ''' markdown_html = self.parser_specific_convert(markdown_text) image_convert = self.settings.get("image_path_conversion", "absolute") file_convert = self.settings.get("file_path_conversions", "absolute") markdown_html = self.parser_specific_postprocess(markdown_html) if "absolute" in (image_convert, file_convert): markdown_html = self.postprocessor_pathconverter( markdown_html, image_convert, file_convert, True) if "relative" in (image_convert, file_convert): markdown_html = self.postprocessor_pathconverter( markdown_html, image_convert, file_convert, False) if image_convert == "base64": markdown_html = self.postprocessor_base64(markdown_html) if self.settings.get("html_simple", False): markdown_html = self.postprocessor_simple(markdown_html) return markdown_html def get_title(self): if self.meta_title is not None: title = self.meta_title else: title = self.view.name() if not title: fn = self.view.file_name() title = 'untitled' if not fn else os.path.splitext( os.path.basename(fn))[0] return '<title>%s</title>' % cgi.escape(title) def get_meta(self): self.meta_title = None meta = [] for k, v in self.settings.get("meta", {}).items(): if k == "title": if isinstance(v, list): if len(v) == 0: v = "" else: v = v[0] self.meta_title = unicode_str(v) continue if isinstance(v, list): v = ','.join(v) if v is not None: meta.append('<meta name="%s" content="%s">' % (cgi.escape(k, True), cgi.escape(v, True))) return '\n'.join(meta) def run(self, view, wholefile=False, preview=False): ''' return full html and body html for view. ''' self.settings = Settings('MarkdownPreview.sublime-settings', view.file_name()) self.preview = preview self.view = view contents = self.get_contents(wholefile) body = self.convert_markdown(contents) html_template = self.settings.get('html_template') # use customized html template if given if self.settings.get('html_simple', False): html = body elif html_template and os.path.exists(html_template): head = u'' head += self.get_meta() if not self.settings.get('skip_default_stylesheet'): head += self.get_stylesheet() head += self.get_javascript() head += self.get_highlight() head += self.get_mathjax() head += self.get_uml() head += self.get_title() html = load_utf8(html_template) html = html.replace('{{ HEAD }}', head, 1) html = html.replace('{{ BODY }}', body, 1) else: html = u'<!DOCTYPE html>' html += '<html><head><meta charset="utf-8">' html += self.get_meta() html += self.get_stylesheet() html += self.get_javascript() html += self.get_highlight() html += self.get_mathjax() html += self.get_uml() html += self.get_title() html += '</head><body>' html += '<article class="markdown-body">' html += body html += '</article>' html += '</body>' html += '</html>' return html, body
class Compiler(object): ''' Do the markdown converting ''' default_css = "markdown.css" def isurl(self, css_name): match = re.match(r'https?://', css_name) if match: return True return False def get_default_css(self): ''' locate the correct CSS with the 'css' setting ''' css_name = self.settings.get('css', 'default') if self.isurl(css_name): # link to remote URL return u"<link href='%s' rel='stylesheet' type='text/css'>" % css_name elif os.path.isfile(os.path.expanduser(css_name)): # use custom CSS file return u"<style>%s</style>" % load_utf8(os.path.expanduser(css_name)) elif css_name == 'default': # use parser CSS file return u"<style>%s</style>" % load_resource(self.default_css) return '' def get_override_css(self): ''' handls allow_css_overrides setting. ''' if self.settings.get('allow_css_overrides'): filename = self.view.file_name() filetypes = self.settings.get('markdown_filetypes') if filename and filetypes: for filetype in filetypes: if filename.endswith(filetype): css_filename = filename.rpartition(filetype)[0] + '.css' if (os.path.isfile(css_filename)): return u"<style>%s</style>" % load_utf8(css_filename) return '' def get_stylesheet(self): ''' return the correct CSS file based on parser and settings ''' return self.get_default_css() + self.get_override_css() def get_javascript(self): js_files = self.settings.get('js') scripts = '' if js_files is not None: # Ensure string values become a list. if isinstance(js_files, str) or isinstance(js_files, unicode_str): js_files = [js_files] # Only load scripts if we have a list. if isinstance(js_files, list): for js_file in js_files: if os.path.isabs(js_file): # Load the script inline to avoid cross-origin. scripts += u"<script>%s</script>" % load_utf8(js_file) else: scripts += u"<script type='text/javascript' src='%s'></script>" % js_file return scripts def get_mathjax(self): ''' return the MathJax script if enabled ''' if self.settings.get('enable_mathjax') is True: return load_resource('mathjax.html') return '' def get_highlight(self): return '' def get_contents(self, wholefile=False): ''' Get contents or selection from view and optionally strip the YAML front matter ''' region = sublime.Region(0, self.view.size()) contents = self.view.substr(region) if not wholefile: # use selection if any selection = self.view.substr(self.view.sel()[0]) if selection.strip() != '': contents = selection # Remove yaml front matter if self.settings.get('strip_yaml_front_matter') and contents.startswith('---'): frontmatter, contents = self.preprocessor_yaml_frontmatter(contents) self.settings.apply_frontmatter(frontmatter) references = self.settings.get('builtin').get('references', []) for ref in references: contents += get_references(ref) contents = self.parser_specific_preprocess(contents) return contents def parser_specific_preprocess(self, text): return text def preprocessor_yaml_frontmatter(self, text): """ Get frontmatter from string """ frontmatter = {} if text.startswith("---"): m = re.search(r'^(---(.*?)---\r?\n)', text, re.DOTALL) if m: try: frontmatter = yaml.load(m.group(2)) except: print(traceback.format_exc()) text = text[m.end(1):] return frontmatter, text def parser_specific_postprocess(self, text): return text def postprocessor_absolute(self, html, image_convert, file_convert): ''' fix relative paths in images, scripts, and links for the internal parser ''' def tag_fix(m): tag = m.group('tag') src = m.group('src') filename = self.view.file_name() if filename: if ( not src.startswith(ABS_EXCLUDE) and not (sublime.platform() == "windows" and RE_WIN_DRIVE.match(src) is not None) ): # Don't explicitly add file:// prefix, # But don't remove them either abs_path = u'%s/%s' % (os.path.dirname(filename), src) # Don't replace just the first instance, # but explicitly place it where it was before # to ensure a file name 'img' dosen't replace # the tag name etc. if os.path.exists(abs_path): tag = m.group('begin') + abs_path + m.group('end') return tag # Compile the appropriate regex to find images and/or files RE_WIN_DRIVE = re.compile(r"(^[A-Za-z]{1}:(?:\\|/))") RE_SOURCES = re.compile( r"""(?P<tag>(?P<begin><(?:%s%s%s)[^>]+(?:src%s)=["'])(?P<src>[^"']+)(?P<end>[^>]*>))""" % ( r"img" if image_convert else "", r"|" if image_convert and file_convert else "", r"script|a" if file_convert else "", r"|href" if file_convert else "" ) ) return RE_SOURCES.sub(tag_fix, html) def postprocessor_base64(self, html): ''' convert resources (currently images only) to base64 ''' file_types = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif" } exclusion_list = tuple( ['https://', 'http://', '#'] + ["data:%s;base64," % ft for ft in file_types.values()] ) def b64(m): import base64 src = m.group('src') data = m.group('tag') base_path = self.settings.get("basepath") if base_path is None: base_path = "" # Format the link absolute = False if src.startswith('file://'): src = src.replace('file://', '', 1) if sublime.platform() == "windows" and not src.startswith('//'): src = src.lstrip("/") absolute = True elif sublime.platform() == "windows" and RE_WIN_DRIVE.match(src) is not None: absolute = True # Make sure we are working with an absolute path if not src.startswith(exclusion_list): if absolute: src = os.path.normpath(src) else: src = os.path.normpath(os.path.join(base_path, src)) if os.path.exists(src): ext = os.path.splitext(src)[1].lower() if ext in file_types: try: with open(src, "rb") as f: data = m.group('begin') + "data:%s;base64,%s" % ( file_types[ext], base64.b64encode(f.read()).decode('ascii') ) + m.group('end') except Exception: pass return data RE_WIN_DRIVE = re.compile(r"(^[A-Za-z]{1}:(?:\\|/))") RE_SOURCES = re.compile(r"""(?P<tag>(?P<begin><(?:img)[^>]+(?:src)=["'])(?P<src>[^"']+)(?P<end>[^>]*>))""") return RE_SOURCES.sub(b64, html) def postprocessor_simple(self, html): ''' Strip out ids and classes for a simplified HTML output ''' def strip_html(m): tag = m.group('open') if m.group('attr1'): tag += m.group('attr1') if m.group('attr2'): tag += m.group('attr2') if m.group('attr3'): tag += m.group('attr3') if m.group('attr4'): tag += m.group('attr4') if m.group('attr5'): tag += m.group('attr5') if m.group('attr6'): tag += m.group('attr6') tag += m.group('close') return tag # Strip out id, class and style attributes for a simple html output # Since we are stripping out two attributes, we need to set up the groups in such # a way so we can retrieve the data we don't want to throw away # up to these worst case scenarios: # # <tag attr=""... (id|class|style)=""... attr=""... (id|class|style)=""... attr=""... (id|class|style)=""...> # <tag (id|class|style)=""... attr=""... (id|class|style)=""... attr=""... (id|class|style)=""... attr=""...> STRIP_HTML = re.compile( r''' (?P<open><[\w\:\.\-]+) # Tag open (?: (?P<attr1>(?:\s+(?!id|class|style)[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*) # Attributes to keep | (?P<target1>\s+(?:id|class|style)(?:\s*=\s*(?:"[^"]*"|'[^']*'))*) # Attributes to delte ) (?: (?P<attr2>(?:\s+(?!id|class|style)[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*) # Attributes to keep | (?P<target2>\s+(?:id|class|style)(?:\s*=\s*(?:"[^"]*"|'[^']*'))*) # Attributes to delte )? (?: (?P<attr3>(?:\s+(?!id|class|style)[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*) # Attributes to keep | (?P<target3>\s+(?:id|class|style)(?:\s*=\s*(?:"[^"]*"|'[^']*'))*) # Attributes to delte )? (?: (?P<attr4>(?:\s+(?!id|class|style)[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*) # Attributes to keep | (?P<target4>\s+(?:id|class|style)(?:\s*=\s*(?:"[^"]*"|'[^']*'))*) # Attributes to delte )? (?: (?P<attr5>(?:\s+(?!id|class|style)[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*) # Attributes to keep | (?P<target5>\s+(?:id|class|style)(?:\s*=\s*(?:"[^"]*"|'[^']*'))*) # Attributes to delte )? (?: (?P<attr6>(?:\s+(?!id|class|style)[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*) # Attributes to keep | (?P<target6>\s+(?:id|class|style)(?:\s*=\s*(?:"[^"]*"|'[^']*'))*) # Attributes to delte )? (?P<close>\s*(?:\/?)>) # Tag end ''', re.MULTILINE | re.DOTALL | re.VERBOSE ) return STRIP_HTML.sub(strip_html, html) def convert_markdown(self, markdown_text): ''' convert input markdown to HTML, with github or builtin parser ''' markdown_html = self.parser_specific_convert(markdown_text) image_convert = self.settings.get("image_path_conversion", "absolute") file_convert = self.settings.get("file_path_conversions", "absolute") markdown_html = self.parser_specific_postprocess(markdown_html) if "absolute" in (image_convert, file_convert): markdown_html = self.postprocessor_absolute(markdown_html, image_convert, file_convert) if image_convert == "base64": markdown_html = self.postprocessor_base64(markdown_html) if self.settings.get("html_simple", False): markdown_html = self.postprocessor_simple(markdown_html) return markdown_html def get_title(self): if self.meta_title is not None: title = self.meta_title else: title = self.view.name() if not title: fn = self.view.file_name() title = 'untitled' if not fn else os.path.splitext(os.path.basename(fn))[0] return '<title>%s</title>' % cgi.escape(title) def get_meta(self): self.meta_title = None meta = [] for k, v in self.settings.get("meta", {}).items(): if k == "title": if isinstance(v, list): if len(v) == 0: v = "" else: v = v[0] self.meta_title = unicode_str(v) continue if isinstance(v, list): v = ','.join(v) if v is not None: meta.append( '<meta name="%s" content="%s">' % (cgi.escape(k, True), cgi.escape(v, True)) ) return '\n'.join(meta) def run(self, view, wholefile=False): ''' return full html and body html for view. ''' self.settings = Settings('MarkdownPreview.sublime-settings', view.file_name()) self.view = view contents = self.get_contents(wholefile) body = self.convert_markdown(contents) html_template = self.settings.get('html_template') # use customized html template if given if self.settings.get('html_simple', False): html = body elif html_template and os.path.exists(html_template): head = u'' head += self.get_meta() if not self.settings.get('skip_default_stylesheet'): head += self.get_stylesheet() head += self.get_javascript() head += self.get_highlight() head += self.get_mathjax() head += self.get_title() html = load_utf8(html_template) html = html.replace('{{ HEAD }}', head, 1) html = html.replace('{{ BODY }}', body, 1) else: html = u'<!DOCTYPE html>' html += '<html><head><meta charset="utf-8">' html += self.get_meta() html += self.get_stylesheet() html += self.get_javascript() html += self.get_highlight() html += self.get_mathjax() html += self.get_title() html += '</head><body>' html += '<article class="markdown-body">' html += body html += '</article>' html += '</body>' html += '</html>' return html, body