| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- 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"
|