File size: 4,926 Bytes
2e92f3b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | from typing import Any, ClassVar, Dict, Optional, Tuple, Literal
from ..core import BaseRenderer, BlockState
from ..util import escape as escape_text
from ..util import safe_entity, striptags
class HTMLRenderer(BaseRenderer):
"""A renderer for converting Markdown to HTML."""
_escape: bool
NAME: ClassVar[Literal["html"]] = "html"
HARMFUL_PROTOCOLS: ClassVar[Tuple[str, ...]] = (
"javascript:",
"vbscript:",
"file:",
"data:",
)
GOOD_DATA_PROTOCOLS: ClassVar[Tuple[str, ...]] = (
"data:image/gif;",
"data:image/png;",
"data:image/jpeg;",
"data:image/webp;",
)
def __init__(self, escape: bool = True, allow_harmful_protocols: Optional[bool] = None) -> None:
super(HTMLRenderer, self).__init__()
self._allow_harmful_protocols = allow_harmful_protocols
self._escape = escape
def render_token(self, token: Dict[str, Any], state: BlockState) -> str:
# 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 escape_text(url)
_url = url.lower()
if self._allow_harmful_protocols and _url.startswith(tuple(self._allow_harmful_protocols)):
return escape_text(url)
if _url.startswith(self.HARMFUL_PROTOCOLS) and not _url.startswith(self.GOOD_DATA_PROTOCOLS):
return "#harmful-link"
return escape_text(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: Optional[str] = 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: Optional[str] = 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>" + escape_text(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: Any) -> 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: Optional[str] = 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.strip()) + "</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: Any) -> 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"
|