|
| 1 | +#!/usr/bin/env python3 |
| 2 | +# -*- coding: utf-8 -*- |
| 3 | +# Copyright (c) 2019-2021 Oak Ridge National Laboratory, UT-Battelle, LLC. |
| 4 | +# License-Filename: LICENSE |
| 5 | +# SPDX-License-Identifier: MIT |
| 6 | +""" |
| 7 | +Monkey-patch sphinx to shoehorn latex output into an ORNL TM compatible style. |
| 8 | +""" |
| 9 | + |
| 10 | +import sphinx |
| 11 | + |
| 12 | +try: |
| 13 | + from sphinx.writers.html5 import HTML5Translator |
| 14 | + from sphinx.writers.latex import LaTeXTranslator, Table |
| 15 | + from sphinx.builders.latex.transforms import BibliographyTransform |
| 16 | +except ImportError as e: |
| 17 | + print("ERROR: failed to import writers/builders:", e) |
| 18 | + LaTeXTranslator = Table = BibliographyTransform = None |
| 19 | + |
| 20 | +def monkey(cls, replace=True): |
| 21 | + if cls is None: |
| 22 | + def _monkey(func): |
| 23 | + return func |
| 24 | + else: |
| 25 | + def _monkey(func, cls=cls, replace=replace): |
| 26 | + exists = hasattr(cls, func.__name__) |
| 27 | + if exists != replace: |
| 28 | + print("ERROR: class {} {} method {}".format( |
| 29 | + cls.__name__, "has no" if replace else "already has a", |
| 30 | + func.__name__)) |
| 31 | + else: |
| 32 | + # print("Applying patch to {}.{}".format( |
| 33 | + # cls.__name__, func.__name__)) |
| 34 | + setattr(cls, func.__name__, func) |
| 35 | + return func |
| 36 | + return _monkey |
| 37 | + |
| 38 | +@monkey(LaTeXTranslator) |
| 39 | +def visit_desc_annotation(self, node): |
| 40 | + self.body.append(r'\sphinxannotation{') |
| 41 | + |
| 42 | +@monkey(LaTeXTranslator) |
| 43 | +def depart_desc_annotation(self, node): |
| 44 | + self.body.append(r'}') |
| 45 | + |
| 46 | +@monkey(LaTeXTranslator, replace=False) |
| 47 | +def visit_enquote(self, node): |
| 48 | + self.body.append(r'``') |
| 49 | + |
| 50 | +@monkey(LaTeXTranslator, replace=False) |
| 51 | +def depart_enquote(self, node): |
| 52 | + self.body.append(r"''") |
| 53 | + |
| 54 | +@monkey(HTML5Translator, replace=False) |
| 55 | +def visit_enquote(self, node): |
| 56 | + self.body.append(r'&ldquot;') |
| 57 | + |
| 58 | +@monkey(HTML5Translator, replace=False) |
| 59 | +def depart_enquote(self, node): |
| 60 | + self.body.append(r"&rdquot;") |
| 61 | + |
| 62 | +# Replace bibliography's enclosing section rather than moving after appendices |
| 63 | +@monkey(BibliographyTransform) |
| 64 | +def run(self, **kwargs): |
| 65 | + from docutils import nodes |
| 66 | + from sphinx.builders.latex.nodes import thebibliography |
| 67 | + citations = thebibliography() |
| 68 | + section_parent = None |
| 69 | + for node in self.document.traverse(nodes.citation): |
| 70 | + parent = node.parent |
| 71 | + parent.remove(node) |
| 72 | + citations += node |
| 73 | + if section_parent is None: |
| 74 | + # Find first section parent |
| 75 | + while parent: |
| 76 | + if isinstance(parent, nodes.section): |
| 77 | + section_parent = parent |
| 78 | + break |
| 79 | + parent = parent.parent |
| 80 | + |
| 81 | + if section_parent and len(citations) > 0: |
| 82 | + section_parent.replace_self(citations) |
| 83 | + |
| 84 | + |
| 85 | +@monkey(LaTeXTranslator) |
| 86 | +def visit_colspec(self, node): |
| 87 | + # type: (nodes.Node) -> None |
| 88 | + self.table.colcount += 1 |
| 89 | + if 'colwidth' in node: |
| 90 | + self.table.colwidths.append(node['colwidth']) |
| 91 | + if 'stub' in node: |
| 92 | + self.table.stubs.append(self.table.colcount - 1) |
| 93 | + |
| 94 | +@monkey(LaTeXTranslator) |
| 95 | +def depart_row(self, node): |
| 96 | + # Don't add horizontal rules between rows |
| 97 | + self.body.append('\\\\\n') |
| 98 | + self.table.row += 1 |
| 99 | + |
| 100 | +@monkey(Table) |
| 101 | +def get_colspec(self): |
| 102 | + if self.colspec: |
| 103 | + return self.colspec |
| 104 | + if self.get_table_type() == 'tabulary': |
| 105 | + # sphinx.sty sets T to be J by default. |
| 106 | + return '{' + ('T' * self.colcount) + '}\n' |
| 107 | + return '{' + ('l' * self.colcount) + '}\n' |
0 commit comments