日本語のreStructuredTextからdocutilsでhtmlを作成した時の不自然な空白を取り除く
sphinx(日本のユーザ会)では元ネタのrestructuredtextで、日本語の途中で改行を入れると、そこに空欄の文字が入ってしまい、ブラウザで見るときにちょっと見た目が悪くなったりすることへの対応方法が、検索すると見当たります。
- Shibu's Diary: 日本語でSphinxを使う時のストレスを減らす拡張機能
- sphinxjp / website / issues / #34 - japanesesupport.py が問答無用過ぎ? — Bitbucket
- 2012年1月時点で日本語でSphinxを使う時のストレスを減らす拡張機能 - Hack like a rolling stone
- 今日も明日もググったー: sphinx doctree
- Sphinxと改行 - そこはかとなく書くよ。
sphinxを介さず、docutilsでhtml作成する時に対応するスクリプトを書いてみました。
対象となるrestructuredtextは標準出力から読み取って、作成後のhtmlは標準出力に書き出します。
#!/usr/bin/python from docutils.core import publish_doctree, publish_from_doctree from docutils.nodes import paragraph, Text import sys, codecs, re, optparse def get_doc_tree(src, encoding="utf-8-sig"): return publish_doctree(codecs.EncodedFile(src, encoding).read()) # in order no to change literals, select only 'paragraph' nodes # def get_para_list(dtree): para_list = [] for node in dtree.traverse(): if isinstance(node, paragraph): para_list.append(node) return para_list def strip_spaces_between_uchars(para_list): # non-ascii [\n\r\t] non-ascii __RGX = re.compile(r'([^!-~])[\n\r\t]+([^!-~])') # modify text inside Text node # for para in para_list: for node in para.traverse(): if isinstance(node, Text): newtext = node.astext() newtext = __RGX.sub(r"\1\2", newtext) node.parent.replace(node, Text(newtext)) def strip_spaces_around_uchars_paragraph_children(para_list): # non-ascii [\s]* <End-of-TEXT> __RGX1 = re.compile(r'([^!-~])[\s]*$') # <Beginning-of-TEXT> [\s]* non-ascii __RGX2 = re.compile(r'^[\s]*([^!-~])') # modify texts over 2 nodes # (paragraph node can have childre of Inline (reference, etc) nodes) # for para in para_list: prev_textnode = Text("") for node in para.traverse(): new_textnode = None if isinstance(node, Text): prevtext = prev_textnode.astext() newtext = node.astext() if __RGX1.search(prevtext) and __RGX2.search(newtext): new_prev_textnode = Text(prev_textnode.astext().rstrip()) new_textnode = Text(newtext.lstrip()) prev_textnode.parent.replace(prev_textnode, new_prev_textnode) node.parent.replace(node, new_textnode) new_prev_textnode.parent = prev_textnode.parent new_textnode.parent = node.parent prev_textnode = new_textnode if new_textnode else node def output_html_from_doctree(dtree, encoding="utf-8"): settings_overrides = { 'output_encoding': encoding } return publish_from_doctree(dtree, writer_name='html', settings_overrides = settings_overrides) if __name__ == '__main__': optparser = optparse.OptionParser() optparser.add_option('-f', "--from-encoding", dest="from_encoding", default="utf-8-sig",) optparser.add_option('-t', "--to-encoding", dest="to_encoding", default="utf-8",) options, remainder = optparser.parse_args() dtree = get_doc_tree(sys.stdin, options.from_encoding) para_list = get_para_list(dtree) strip_spaces_between_uchars(para_list) strip_spaces_around_uchars_paragraph_children(para_list) html_str = output_html_from_doctree(dtree, options.to_encoding) sys.stdout.write(html_str)
「A ReStructuredText Primer」の日本語訳「ReStructuredText 入門」の「元ネタファイル」で、エラーが出なくて、意図した通りに動作しているっぽいことを確認。
ちなみに「スペース無しでもマークアップとして認識できるようにしてみた」という修正は「日本語でreStructuredText」にあります(私が作った訳じゃないですが)。
*1:2014/2/11、-fで入力データのencoding、-tで出力データのencodingを指定できるようにしました