diff options
Diffstat (limited to 'doc/rub')
-rwxr-xr-x | doc/rub | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/doc/rub b/doc/rub new file mode 100755 index 0000000..d1113a2 --- /dev/null +++ b/doc/rub @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +from contextlib import contextmanager +from functools import partial +from pathlib import Path +from shutil import which +from subprocess import PIPE, Popen +from urllib.request import Request, urlopen +from xml.sax.saxutils import unescape + +from lxml.etree import Element, XML +from lxml.html import (fragment_fromstring as from_html_fragment, + fragments_fromstring as from_html_fragments) +from mistune import html as from_md +from pygments import highlight +from pygments.formatters import HtmlFormatter +from pygments.lexers import get_lexer_by_name, guess_lexer +from rub import rub, xml + +PYGMENTS_FORMATTER = HtmlFormatter(lineseparator='<br/>') +KATEX_SERVER = ''' +var http = require('http'); +var katex = require('katex'); + +let server = http.createServer((request, response) => { + if (request.method === 'POST') { + response.writeHead(200, 'OK'); + var body = ''; + request.on('data', (chunk) => body += chunk); + request.on('end', () => { + response.write(katex.renderToString(body, { + displayMode: request.headers['x-katex-mode'] === 'display', + output: 'mathml', + })); + response.end(); + }); + } else { + response.writeHead(405, 'Method Not Allowed'); + response.end(); + } +}); +server.listen(() => { + console.log('http://localhost:' + server.address().port); +}); +''' + + +def mistune(extension, context, input_node, output_parent): + """Render Markdown to HTML.""" + tmp = Element('tmp') + xml.recurse(extension, context, input_node, tmp) + tmp[0].text = input_node.text + text = xml.serialize_content(tmp[0]).decode() + for i in from_html_fragments(from_md(unescape(text))): + extension.apply_templates(context, i, output_parent) + + +def pygments(extension, context, input_node, output_parent): + """Highlight code syntax in HTML.""" + code = input_node.text + lang = input_node.get('lang') + lexer = guess_lexer(code) if lang is None else get_lexer_by_name(lang) + output = from_html_fragment(highlight(code, lexer, PYGMENTS_FORMATTER)) + output.tail = input_node.tail + output_parent.append(output) + + +def katex(extension, context, input_node, output_parent, url, display=False): + """Render LaTeX to content MathML.""" + with urlopen(Request(url, xml.serialize_content(input_node), + {'X-KaTeX-Mode': 'display'} if display else {})) as r: + output = XML(r.read())[0] # remove span[@class='katex'] + output.tail = input_node.tail + output_parent.append(output) + + +@contextmanager +def katex_server(): + server = Popen(which('node'), stdin=PIPE, stdout=PIPE, text=True) + try: + server.stdin.write(KATEX_SERVER) + server.stdin.close() + yield partial(katex, url=server.stdout.readline().strip()) + finally: + server.kill() + + +with katex_server() as mathml: + wd = Path(__file__).parent + rub(xml.Processor(wd/'html.xslt', + partial(Path.with_suffix, suffix='.html'), + markdown=mistune, highlight=pygments, + math=partial(mathml, display=True), m=mathml), + xml.Processor(wd/'rss.xslt', + partial(Path.with_name, name='index.xml')), + wd/'base', wd/'pages', wd/'cache', wd/'out') |