You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			177 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			177 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Python
		
	
import re
 | 
						|
from typing import TYPE_CHECKING, Any, Dict, List, Match, Optional
 | 
						|
 | 
						|
from ..util import escape as escape_text
 | 
						|
from ..util import escape_url
 | 
						|
from ._base import BaseDirective, DirectivePlugin
 | 
						|
 | 
						|
if TYPE_CHECKING:
 | 
						|
    from ..block_parser import BlockParser
 | 
						|
    from ..core import BlockState
 | 
						|
    from ..markdown import Markdown
 | 
						|
    from ..renderers.html import HTMLRenderer
 | 
						|
 | 
						|
__all__ = ["Image", "Figure"]
 | 
						|
 | 
						|
_num_re = re.compile(r"^\d+(?:\.\d*)?")
 | 
						|
_allowed_aligns = ["top", "middle", "bottom", "left", "center", "right"]
 | 
						|
 | 
						|
 | 
						|
def _parse_attrs(options: Dict[str, Any]) -> Dict[str, Any]:
 | 
						|
    attrs = {}
 | 
						|
    if "alt" in options:
 | 
						|
        attrs["alt"] = options["alt"]
 | 
						|
 | 
						|
    # validate align
 | 
						|
    align = options.get("align")
 | 
						|
    if align and align in _allowed_aligns:
 | 
						|
        attrs["align"] = align
 | 
						|
 | 
						|
    height = options.get("height")
 | 
						|
    width = options.get("width")
 | 
						|
    if height and _num_re.match(height):
 | 
						|
        attrs["height"] = height
 | 
						|
    if width and _num_re.match(width):
 | 
						|
        attrs["width"] = width
 | 
						|
    if "target" in options:
 | 
						|
        attrs["target"] = escape_url(options["target"])
 | 
						|
    return attrs
 | 
						|
 | 
						|
 | 
						|
class Image(DirectivePlugin):
 | 
						|
    NAME = "image"
 | 
						|
 | 
						|
    def parse(self, block: "BlockParser", m: Match[str], state: "BlockState") -> Dict[str, Any]:
 | 
						|
        options = dict(self.parse_options(m))
 | 
						|
        attrs = _parse_attrs(options)
 | 
						|
        attrs["src"] = self.parse_title(m)
 | 
						|
        return {"type": "block_image", "attrs": attrs}
 | 
						|
 | 
						|
    def __call__(self, directive: "BaseDirective", md: "Markdown") -> None:
 | 
						|
        directive.register(self.NAME, self.parse)
 | 
						|
        assert md.renderer is not None
 | 
						|
        if md.renderer.NAME == "html":
 | 
						|
            md.renderer.register("block_image", render_block_image)
 | 
						|
 | 
						|
 | 
						|
def render_block_image(
 | 
						|
    self: "HTMLRenderer",
 | 
						|
    src: str,
 | 
						|
    alt: Optional[str] = None,
 | 
						|
    width: Optional[str] = None,
 | 
						|
    height: Optional[str] = None,
 | 
						|
    **attrs: Any,
 | 
						|
) -> str:
 | 
						|
    img = '<img src="' + escape_text(src) + '"'
 | 
						|
    style = ""
 | 
						|
    if alt:
 | 
						|
        img += ' alt="' + escape_text(alt) + '"'
 | 
						|
    if width:
 | 
						|
        if width.isdigit():
 | 
						|
            img += ' width="' + width + '"'
 | 
						|
        else:
 | 
						|
            style += "width:" + width + ";"
 | 
						|
    if height:
 | 
						|
        if height.isdigit():
 | 
						|
            img += ' height="' + height + '"'
 | 
						|
        else:
 | 
						|
            style += "height:" + height + ";"
 | 
						|
    if style:
 | 
						|
        img += ' style="' + escape_text(style) + '"'
 | 
						|
 | 
						|
    img += " />"
 | 
						|
 | 
						|
    _cls = "block-image"
 | 
						|
    align = attrs.get("align")
 | 
						|
    if align:
 | 
						|
        _cls += " align-" + align
 | 
						|
 | 
						|
    target = attrs.get("target")
 | 
						|
    if target:
 | 
						|
        href = self.safe_url(target)
 | 
						|
        outer = '<a class="' + _cls + '" href="' + href + '">'
 | 
						|
        return outer + img + "</a>\n"
 | 
						|
    else:
 | 
						|
        return '<div class="' + _cls + '">' + img + "</div>\n"
 | 
						|
 | 
						|
 | 
						|
class Figure(DirectivePlugin):
 | 
						|
    NAME = "figure"
 | 
						|
 | 
						|
    def parse_directive_content(
 | 
						|
        self, block: "BlockParser", m: Match[str], state: "BlockState"
 | 
						|
    ) -> Optional[List[Dict[str, Any]]]:
 | 
						|
        content = self.parse_content(m)
 | 
						|
        if not content:
 | 
						|
            return None
 | 
						|
 | 
						|
        tokens = list(self.parse_tokens(block, content, state))
 | 
						|
        caption = tokens[0]
 | 
						|
        if caption["type"] == "paragraph":
 | 
						|
            caption["type"] = "figcaption"
 | 
						|
            children = [caption]
 | 
						|
            if len(tokens) > 1:
 | 
						|
                children.append({"type": "legend", "children": tokens[1:]})
 | 
						|
            return children
 | 
						|
        return None
 | 
						|
 | 
						|
    def parse(self, block: "BlockParser", m: Match[str], state: "BlockState") -> Dict[str, Any]:
 | 
						|
        options = dict(self.parse_options(m))
 | 
						|
        image_attrs = _parse_attrs(options)
 | 
						|
        image_attrs["src"] = self.parse_title(m)
 | 
						|
 | 
						|
        align = image_attrs.pop("align", None)
 | 
						|
        fig_attrs = {}
 | 
						|
        if align:
 | 
						|
            fig_attrs["align"] = align
 | 
						|
        for k in ["figwidth", "figclass"]:
 | 
						|
            if k in options:
 | 
						|
                fig_attrs[k] = options[k]
 | 
						|
 | 
						|
        children = [{"type": "block_image", "attrs": image_attrs}]
 | 
						|
        content = self.parse_directive_content(block, m, state)
 | 
						|
        if content:
 | 
						|
            children.extend(content)
 | 
						|
        return {
 | 
						|
            "type": "figure",
 | 
						|
            "attrs": fig_attrs,
 | 
						|
            "children": children,
 | 
						|
        }
 | 
						|
 | 
						|
    def __call__(self, directive: "BaseDirective", md: "Markdown") -> None:
 | 
						|
        directive.register(self.NAME, self.parse)
 | 
						|
 | 
						|
        assert md.renderer is not None
 | 
						|
        if md.renderer.NAME == "html":
 | 
						|
            md.renderer.register("figure", render_figure)
 | 
						|
            md.renderer.register("block_image", render_block_image)
 | 
						|
            md.renderer.register("figcaption", render_figcaption)
 | 
						|
            md.renderer.register("legend", render_legend)
 | 
						|
 | 
						|
 | 
						|
def render_figure(
 | 
						|
    self: Any,
 | 
						|
    text: str,
 | 
						|
    align: Optional[str] = None,
 | 
						|
    figwidth: Optional[str] = None,
 | 
						|
    figclass: Optional[str] = None,
 | 
						|
) -> str:
 | 
						|
    _cls = "figure"
 | 
						|
    if align:
 | 
						|
        _cls += " align-" + align
 | 
						|
    if figclass:
 | 
						|
        _cls += " " + figclass
 | 
						|
 | 
						|
    html = '<figure class="' + _cls + '"'
 | 
						|
    if figwidth:
 | 
						|
        html += ' style="width:' + figwidth + '"'
 | 
						|
    return html + ">\n" + text + "</figure>\n"
 | 
						|
 | 
						|
 | 
						|
def render_figcaption(self: Any, text: str) -> str:
 | 
						|
    return "<figcaption>" + text + "</figcaption>\n"
 | 
						|
 | 
						|
 | 
						|
def render_legend(self: Any, text: str) -> str:
 | 
						|
    return '<div class="legend">\n' + text + "</div>\n"
 |