Skip to content

Instantly share code, notes, and snippets.

@osmatsuda
Last active September 9, 2022 03:31
Show Gist options
  • Save osmatsuda/29c37f31ac7f5bbae90b8890baf16e2e to your computer and use it in GitHub Desktop.
Save osmatsuda/29c37f31ac7f5bbae90b8890baf16e2e to your computer and use it in GitHub Desktop.
from docutils.core import publish_string
from docutils.nodes import TextElement, Inline, Text, unescape
from docutils.parsers import rst
from docutils.parsers.rst import directives
from docutils.writers.html4css1 import Writer, HTMLTranslator
import re
# ruby node
class ruby(Inline, TextElement): pass
class rt(Inline, TextElement): pass
class rp(Inline, TextElement): pass
def build_node(rawtext, node, delimiters='()'):
dlm = re.escape(delimiters)
rx = re.compile(f"([^{dlm}]+)(\s*[{dlm}]\s*)([^{dlm}]+)(\s*[{dlm}]\s*)")
for m in re.finditer(rx, unescape(rawtext)):
rbase, rpl, rant, rpr = m.groups()
node += [
Text(rbase),
rp(rawtext, rpl),
rt(rawtext, rant),
rp(rawtext, rpr),
]
return node
# ruby directive
class Ruby(rst.Directive):
required_arguments = 1
option_spec = {'delimiters': directives.unchanged,}
def run(self):
rawtext = self.arguments[0]
node = ruby(rawtext, '', **self.options)
return [build_node(
rawtext,
node,
self.options.get('delimiters')
)]
directives.register_directive('ruby', Ruby)
# rubi role
def rubi_role(role, rawtext, text, lineno, inliner, options=None, content=None):
options = roles.normalized_role_options(options)
node = ruby(rawtext, '', **options)
return [build_node(
rawtext,
node,
options.get('delimiters')
)], []
ruby_role.options = {'delimiters': directives.unchanged}
roles.register_canonical_role('rubi', rubi_role)
# Transformer and Writer
class MyWriter(Writer):
def __init__(self):
self.parts = {}
self.translator_class = MyHTMLTranslator
class MyHTMLTranslator(HTMLTranslator):
def visit_ruby(self, node):
self.body.append(self.starttag(node, 'ruby', suffix=''))
def depart_ruby(self, node):
self.body.append('</ruby>')
def visit_rp(self, node):
self.body.append(self.starttag(node, 'rp', suffix=''))
def depart_rp(self, node):
self.body.append('</rp>')
def visit_rt(self, node):
self.body.append(self.starttag(node, 'rt', suffix=''))
def depart_rt(self, node):
self.body.append('</rt>')
# Publisher
with open(source) as src, \
open(destination, 'wb') as out:
output = publish_string(src.read(), writer=MyWriter())
out.write(output)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment