2021-02-03 01:20:41 +11:00
|
|
|
# Copyright © 2021 Lee Yingtong Li
|
|
|
|
#
|
|
|
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.https://mozilla.org/MPL/2.0/.
|
|
|
|
|
|
|
|
def setup(app):
|
|
|
|
app.add_builder(POStandaloneHTMLBuilder, override=True)
|
|
|
|
app.add_builder(POLaTeXBuilder, override=True)
|
2021-02-10 17:34:38 +11:00
|
|
|
app.add_builder(POEpub3Builder, override=True)
|
2021-02-03 01:20:41 +11:00
|
|
|
|
|
|
|
app.add_role('subref', SubRefRole())
|
|
|
|
|
|
|
|
#app.add_source_parser(PORSTParser, override=True)
|
|
|
|
|
|
|
|
# HTML Builder
|
|
|
|
|
|
|
|
from sphinx.builders.html import StandaloneHTMLBuilder
|
|
|
|
from sphinx.environment.adapters.toctree import TocTree
|
|
|
|
from sphinx.writers.html5 import HTML5Translator
|
|
|
|
|
|
|
|
import docutils.nodes
|
|
|
|
import sphinx.addnodes
|
|
|
|
|
|
|
|
from sphinx.util import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
class POHTML5Translator(HTML5Translator):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
|
|
self.paragraph_num = 0
|
|
|
|
self.footnote_order = []
|
|
|
|
|
|
|
|
def visit_paragraph(self, node):
|
|
|
|
super().visit_paragraph(node)
|
|
|
|
|
|
|
|
if node.parent.tagname == 'section':
|
|
|
|
# Add paragraph numbers
|
|
|
|
self.paragraph_num += 1
|
2021-02-10 22:24:59 +11:00
|
|
|
|
|
|
|
if self.builder.name == 'html':
|
|
|
|
self.body.append('<span class="paragraph-num"><a id="para{0}" href="#para{0}">{0}</a></span>'.format(self.paragraph_num))
|
|
|
|
else: # epub (or mobi)
|
|
|
|
# Hardcode the brackets, as MOBI6 does not support CSS
|
|
|
|
self.body.append('<span class="paragraph-num">[{0}] </span>'.format(self.paragraph_num))
|
2021-02-03 01:20:41 +11:00
|
|
|
|
|
|
|
def visit_footnote_reference(self, node):
|
|
|
|
super().visit_footnote_reference(node)
|
|
|
|
|
|
|
|
# Verify order
|
|
|
|
if node['refid'] not in self.footnote_order:
|
|
|
|
self.footnote_order.append(node['refid'])
|
|
|
|
|
|
|
|
def visit_footnote(self, node):
|
|
|
|
super().visit_footnote(node)
|
|
|
|
|
|
|
|
# Verify order
|
|
|
|
if not self.footnote_order:
|
|
|
|
pass
|
|
|
|
elif node['ids'][0] == self.footnote_order[0]:
|
|
|
|
del self.footnote_order[0]
|
|
|
|
else:
|
|
|
|
logger.warning('footnote defined out of sequence: expecting {}'.format(self.footnote_order[0]), location=node)
|
|
|
|
self.footnote_order = None # Ignore future errors
|
|
|
|
|
|
|
|
class POStandaloneHTMLBuilder(StandaloneHTMLBuilder):
|
|
|
|
default_translator_class = POHTML5Translator
|
|
|
|
|
|
|
|
__TocTree_resolve = TocTree.resolve
|
|
|
|
def _TocTree_resolve(self, *args, **kwargs):
|
|
|
|
result = __TocTree_resolve(self, *args, **kwargs)
|
|
|
|
|
2021-02-10 22:24:59 +11:00
|
|
|
if self.env.app.builder.name == 'html' or self.env.app.builder.name == 'epub':
|
2021-02-03 01:20:41 +11:00
|
|
|
# Add Index TOC entry
|
|
|
|
bullet_list = result[1]
|
2021-02-10 22:24:59 +11:00
|
|
|
reference = docutils.nodes.reference('', '', internal=True, refuri='genindex.xhtml' if self.env.app.builder.name == 'epub' else 'genindex.html', anchorname='', *[docutils.nodes.Text('Index')])
|
2021-02-03 01:20:41 +11:00
|
|
|
compact_paragraph = sphinx.addnodes.compact_paragraph('', '', reference)
|
|
|
|
list_item = docutils.nodes.list_item('', classes=['toctree-l1'], *[compact_paragraph])
|
|
|
|
bullet_list.append(list_item)
|
|
|
|
|
|
|
|
return result
|
|
|
|
TocTree.resolve = _TocTree_resolve
|
|
|
|
|
|
|
|
# LaTeX Builder
|
|
|
|
|
|
|
|
from sphinx.builders.latex import LaTeXBuilder
|
|
|
|
from sphinx.writers.latex import LaTeXTranslator
|
|
|
|
|
|
|
|
class POLaTeXTranslator(LaTeXTranslator):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
|
|
self.paragraph_num = 0
|
|
|
|
|
|
|
|
def visit_title(self, node):
|
|
|
|
super().visit_title(node)
|
|
|
|
|
|
|
|
if self.sectionlevel == 1:
|
|
|
|
# Reset paragraph numbers
|
|
|
|
self.paragraph_num = 0
|
|
|
|
|
|
|
|
def visit_paragraph(self, node):
|
|
|
|
super().visit_paragraph(node)
|
|
|
|
|
|
|
|
if node.parent.tagname == 'section':
|
|
|
|
# Add paragraph numbers
|
|
|
|
self.paragraph_num += 1
|
|
|
|
#self.body.append(r'\makebox[0pt][r]{{\small\textcolor[HTML]{{AAAAAA}}{{¶{0}}}\hspace{{0.8em}}}}'.format(self.paragraph_num))
|
|
|
|
#self.body.append(r'{{\small\textcolor[HTML]{{AAAAAA}}{{[¶{0}]}}}} '.format(self.paragraph_num))
|
|
|
|
self.body.append(r'{{\small\bfseries\sffamily[{0}]}} '.format(self.paragraph_num))
|
|
|
|
|
|
|
|
class POLaTeXBuilder(LaTeXBuilder):
|
|
|
|
default_translator_class = POLaTeXTranslator
|
|
|
|
|
2021-02-10 17:34:38 +11:00
|
|
|
# Epub3 Builder
|
|
|
|
|
|
|
|
from sphinx.builders.epub3 import Epub3Builder
|
|
|
|
|
|
|
|
class POEpub3Builder(Epub3Builder):
|
|
|
|
default_translator_class = POHTML5Translator
|
2021-02-10 22:24:59 +11:00
|
|
|
|
|
|
|
def toc_add_files(self, refnodes):
|
|
|
|
super().toc_add_files(refnodes)
|
|
|
|
|
|
|
|
# Add Index TOC entry
|
|
|
|
refnodes.append({
|
|
|
|
'level': 1,
|
|
|
|
'refuri': 'genindex.xhtml',
|
|
|
|
'text': 'Index'
|
|
|
|
})
|
2021-02-10 17:34:38 +11:00
|
|
|
|
2021-02-03 01:20:41 +11:00
|
|
|
# :subref: Role
|
|
|
|
|
|
|
|
from sphinx.roles import XRefRole
|
|
|
|
|
|
|
|
class SubRefRole(XRefRole):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super().__init__(lowercase=True, innernodeclass=docutils.nodes.inline, warn_dangling=True, *args, **kwargs)
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
self.name = 'std:ref'
|
|
|
|
return super().run()
|
|
|
|
|
|
|
|
def result_nodes(self, document, env, node, is_ref):
|
|
|
|
result = super().result_nodes(document, env, node, is_ref)
|
|
|
|
pending_xref = result[0][0]
|
|
|
|
inline = pending_xref[0]
|
|
|
|
|
|
|
|
substitution_reference = docutils.nodes.substitution_reference('', '', refname=docutils.nodes.fully_normalize_name(self.title))
|
|
|
|
|
|
|
|
inline.clear() # Remove raw "title" text node
|
|
|
|
inline.append(substitution_reference) # Replace with substitution_reference
|
|
|
|
|
|
|
|
pending_xref['po_subref'] = True # Flag for StandardDomain._resolve_ref_xref
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
from sphinx.domains.std import StandardDomain
|
|
|
|
|
|
|
|
__StandardDomain_resolve_ref_xref = StandardDomain._resolve_ref_xref
|
|
|
|
def _StandardDomain_resolve_ref_xref(self, env, fromdocname, builder, typ, target, node, contnode):
|
|
|
|
if 'po_subref' in node:
|
|
|
|
# Build reference node, preserving substitution
|
|
|
|
docname, labelid = self.anonlabels.get(target, ('', ''))
|
|
|
|
|
|
|
|
# Based on StandardDomain.build_reference_node
|
|
|
|
newnode = docutils.nodes.reference('', '', internal=True)
|
|
|
|
if docname == fromdocname:
|
|
|
|
newnode['refid'] = labelid
|
|
|
|
else:
|
|
|
|
contnode = sphinx.addnodes.pending_xref('')
|
|
|
|
contnode['refdocname'] = docname
|
|
|
|
contnode['refsectname'] = node.astext()
|
|
|
|
newnode['refuri'] = builder.get_relative_uri(fromdocname, docname)
|
|
|
|
if labelid:
|
|
|
|
newnode['refuri'] += '#' + labelid
|
|
|
|
|
|
|
|
# Copy across substitution
|
|
|
|
newnode.children.extend(node.children)
|
|
|
|
return newnode
|
|
|
|
|
|
|
|
return __StandardDomain_resolve_ref_xref(self, env, fromdocname, builder, typ, target, node, contnode)
|
|
|
|
StandardDomain._resolve_ref_xref = _StandardDomain_resolve_ref_xref
|
|
|
|
|
|
|
|
# RST Parser
|
|
|
|
|
|
|
|
from sphinx.parsers import RSTParser
|
|
|
|
|
|
|
|
class PORSTParser(RSTParser):
|
|
|
|
def parse(self, inputstring, document):
|
|
|
|
super().parse(inputstring, document)
|
|
|
|
|
|
|
|
# Verify footnotes
|
|
|
|
|
|
|
|
print(document)
|