# coding: utf-8 # Author: Jaroslav Kysela and Jan Mrázek # License: WTFPL 2 """ Markdown renderer ~~~~~~~~~~~~~~~~~ This class renders parsed markdown back to markdown. It is useful for automatic modifications of the md contents. :copyright: (c) 2015 by Jaroslav Kysela (c) extended by Jan Mrázek 2022 :licence: WTFPL 2 """ # The following try-catch is used to support mistune 0.8.4 and 2.x try: from mistune.renderers import BaseRenderer # type: ignore except ModuleNotFoundError: from mistune import Renderer # type: ignore BaseRenderer = Renderer class MdRenderer(BaseRenderer): def __init__(self, **kwargs): super(MdRenderer, self).__init__() def get_block(text): type = text[0] p = text.find(':') if p <= 0: return ('', '', '') l = int(text[1:p]) t = text[p+1:p+1+l] return (text[p+1+l:], type, t) def newline(self): return '\n' def text(self, text): return text def linebreak(self): return '\n' def hrule(self): return '---\n' def heading(self, text, level, raw=None): return '#'*level + " " + text + '\n\n' # Mistune 0.8.4 calls it header, not heading header = heading def paragraph(self, text): return text + '\n\n' def list(self, text, ordered=True, level=None, start=None): r = '\n' indent = ' ' * ((level-1) * 2) while text: text, type, t = MdRenderer.get_block(text) if type == 'l': r += indent + (ordered and ('# ' + t) or ('* ' + t)) + '\n' return r def list_item(self, text, level=None): return 'l' + str(len(text)) + ':' + text def block_text(self, text): return text def block_code(self, code, lang=None): return '```\n' + code + '\n```\n' def block_quote(self, text): r = '' for line in text.splitlines(): r += (line and '> ' or '') + line + '\n' return r def _emphasis(self, text, pref): return pref + text + pref + ' ' def emphasis(self, text): return self._emphasis(text, '_') def double_emphasis(self, text): return self._emphasis(text, '__') def strikethrough(self, text): return self._emphasis(text, '~~') def codespan(self, text): return '`' + text + '`' def autolink(self, link, is_email=False): return '<' + link + '>' def link(self, link, title, text, image=False): if text is None: text = title title = None r = (image and '!' or '') + '[' + text + '](' + link + ')' if title: r += '"' + title + '"' return r def image(self, src, title, text): self.link(src, title, text, image=True) def table(self, header, body): hrows = [] while header: header, type, t = MdRenderer.get_block(header) if type == 'r': flags = {} cols = [] while t: t, type2, t2 = MdRenderer.get_block(t) if type2 == 'f': fl, v = t2.split('=') flags[fl] = v elif type2 == 'c': cols.append( type('', (object,), {'flags': flags, 'text': t2})()) hrows.append(cols) brows = [] while body: body, type, t = MdRenderer.get_block(body) if type == 'r': flags = {} cols = [] while t: t, type2, t2 = MdRenderer.get_block(t) if type2 == 'f': fl, v = t2.split('=') flags[fl] = v elif type2 == 'c': cols.append( type('', (object,), {'flags': flags, 'text': t2})()) brows.append(cols) colscount = 0 colmax = [0] * 100 align = [''] * 100 for row in hrows + brows: colscount = max(len(row), colscount) i = 0 for col in row: colmax[i] = max(len(col.text), colmax[i]) if 'align' in col.flags: align[i] = col.flags['align'][0] i += 1 r = '' for row in hrows: i = 0 for col in row: if i > 0: r += ' | ' r += col.text.ljust(colmax[i]) i += 1 r += '\n' for i in range(colscount): if i > 0: r += ' | ' if align[i] == 'c': r += ':' + '-'.ljust(colmax[i]-2, '-') + ':' elif align[i] == 'l': r += ':' + '-'.ljust(colmax[i]-1, '-') elif align[i] == 'r': r += '-'.ljust(colmax[i]-1, '-') + ':' else: r += '-'.ljust(colmax[i], '-') r += '\n' for row in brows: i = 0 for col in row: if i > 0: r += ' | ' r += col.text.ljust(colmax[i]) i += 1 r += '\n' return r def table_row(self, content): return 'r' + str(len(content)) + ':' + content def table_cell(self, content, **flags): content = content.replace('\n', ' ') r = '' for fl in flags: v = flags[fl] if type(v) == type(True): v = v and 1 or 0 v = str(v) and str(v) or '' r += 'f' + str(len(fl) + 1 + len(v)) + ':' + fl + '=' + v return r + 'c' + str(len(content)) + ':' + content def footnote_ref(self, key, index): return '[^' + str(index) + ']' def footnote_item(self, key, text): r = '[^' + str(index) + ']:\n' for l in text.split('\n'): r += ' ' + l.lstrip().rstrip() + '\n' return r def footnotes(self, text): return text def finalize(self, data): return ''.join(data)