Program Listing for File mdrender.py

Return to documentation for file (pymdtools/mdrender.py)

# coding: utf-8
"""
    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
    :licence: WTFPL 2
"""

if (__package__ in [None, '']) and ('.' not in __name__):
    import mistunege as mistune
else:
    from . import mistunege as mistune

class MdRenderer(mistune.Renderer):

    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 header(self, text, level, raw=None):
        if level == 1:
            return text + '\n' + '=' * len(text) + '\n\n'
        if level == 2:
            return text + '\n' + '-' * len(text) + '\n\n'
        return '#' * (level) + ' ' + text + '\n\n'

    def paragraph(self, text):
        return text.rstrip() + '\n\n'

    def list(self, text, ordered=True):
        r = ''
        while text:
            text, type, t = MdRenderer.get_block(text)
            if type == 'l':
                t = t.strip()
                t = t.replace('\n  + ', '\n    * ')
                t = t.replace('\n- ', '\n  + ')
                r += (ordered and ('# ' + t) or ('- ' + t))
                if r[-1] != '\n':
                    r += '\n'
            else:
                r += '\n'
        if len(r) > 1 and r[1] == '\n':
            r = r[1:]
        return r + '\n'

    def list_item(self, text):
        return 'l' + str(len(text)) + ':' + 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):
        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': type, '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': type, '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], 3)
                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 += '| '
                if i > 0:
                    r += ' | '
                r += col['text'].ljust(colmax[i])
                i += 1
            r += ' |\n'
        for i in range(colscount):
            if i == 0:
                r += '|'
            if i > 0:
                r += '|'
            if align[i] == 'c':
                r += ':' + '-'.ljust(colmax[i], '-') + ':'
            elif align[i] == 'l':
                r += ':' + '-'.ljust(colmax[i] + 1, '-')
            elif align[i] == 'r':
                r += '-'.ljust(colmax[i] + 1, '-') + ':'
            else:
                r += '-'.ljust(colmax[i] + 2, '-')
        r += '|\n'
        for row in brows:
            i = 0
            for col in row:
                if i == 0:
                    r += '| '
                if i > 0:
                    r += ' | '
                r += col['text'].ljust(colmax[i])
                i += 1
            r += ' |\n'
        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