diff options
Diffstat (limited to 'src/mistune/renderers/html.py')
-rw-r--r-- | src/mistune/renderers/html.py | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/src/mistune/renderers/html.py b/src/mistune/renderers/html.py new file mode 100644 index 0000000..c458a4a --- /dev/null +++ b/src/mistune/renderers/html.py @@ -0,0 +1,151 @@ +from ..core import BaseRenderer +from ..util import escape as escape_text, striptags, safe_entity + + +class HTMLRenderer(BaseRenderer): + """A renderer for converting Markdown to HTML.""" + NAME = 'html' + HARMFUL_PROTOCOLS = ( + 'javascript:', + 'vbscript:', + 'file:', + 'data:', + ) + GOOD_DATA_PROTOCOLS = ( + 'data:image/gif;', + 'data:image/png;', + 'data:image/jpeg;', + 'data:image/webp;', + ) + + def __init__(self, escape=True, allow_harmful_protocols=None): + super(HTMLRenderer, self).__init__() + self._allow_harmful_protocols = allow_harmful_protocols + self._escape = escape + + def render_token(self, token, state): + # backward compitable with v2 + func = self._get_method(token['type']) + attrs = token.get('attrs') + + if 'raw' in token: + text = token['raw'] + elif 'children' in token: + text = self.render_tokens(token['children'], state) + else: + if attrs: + return func(**attrs) + else: + return func() + if attrs: + return func(text, **attrs) + else: + return func(text) + + def safe_url(self, url: str) -> str: + """Ensure the given URL is safe. This method is used for rendering + links, images, and etc. + """ + if self._allow_harmful_protocols is True: + return url + + _url = url.lower() + if self._allow_harmful_protocols and \ + _url.startswith(tuple(self._allow_harmful_protocols)): + return url + + if _url.startswith(self.HARMFUL_PROTOCOLS) and \ + not _url.startswith(self.GOOD_DATA_PROTOCOLS): + return '#harmful-link' + return url + + def text(self, text: str) -> str: + if self._escape: + return escape_text(text) + return safe_entity(text) + + def emphasis(self, text: str) -> str: + return '<em>' + text + '</em>' + + def strong(self, text: str) -> str: + return '<strong>' + text + '</strong>' + + def link(self, text: str, url: str, title=None) -> str: + s = '<a href="' + self.safe_url(url) + '"' + if title: + s += ' title="' + safe_entity(title) + '"' + return s + '>' + text + '</a>' + + def image(self, text: str, url: str, title=None) -> str: + src = self.safe_url(url) + alt = escape_text(striptags(text)) + s = '<img src="' + src + '" alt="' + alt + '"' + if title: + s += ' title="' + safe_entity(title) + '"' + return s + ' />' + + def codespan(self, text: str) -> str: + return '<code>' + text + '</code>' + + def linebreak(self) -> str: + return '<br />\n' + + def softbreak(self) -> str: + return '\n' + + def inline_html(self, html: str) -> str: + if self._escape: + return escape_text(html) + return html + + def paragraph(self, text: str) -> str: + return '<p>' + text + '</p>\n' + + def heading(self, text: str, level: int, **attrs) -> str: + tag = 'h' + str(level) + html = '<' + tag + _id = attrs.get('id') + if _id: + html += ' id="' + _id + '"' + return html + '>' + text + '</' + tag + '>\n' + + def blank_line(self) -> str: + return '' + + def thematic_break(self) -> str: + return '<hr />\n' + + def block_text(self, text: str) -> str: + return text + + def block_code(self, code: str, info=None) -> str: + html = '<pre><code' + if info is not None: + info = safe_entity(info.strip()) + if info: + lang = info.split(None, 1)[0] + html += ' class="language-' + lang + '"' + return html + '>' + escape_text(code) + '</code></pre>\n' + + def block_quote(self, text: str) -> str: + return '<blockquote>\n' + text + '</blockquote>\n' + + def block_html(self, html: str) -> str: + if self._escape: + return '<p>' + escape_text(html) + '</p>\n' + return html + '\n' + + def block_error(self, text: str) -> str: + return '<div class="error"><pre>' + text + '</pre></div>\n' + + def list(self, text: str, ordered: bool, **attrs) -> str: + if ordered: + html = '<ol' + start = attrs.get('start') + if start is not None: + html += ' start="' + str(start) + '"' + return html + '>\n' + text + '</ol>\n' + return '<ul>\n' + text + '</ul>\n' + + def list_item(self, text: str) -> str: + return '<li>' + text + '</li>\n' |