Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2"""
3@file
4@brief Overwrites latex writer as Sphinx's version is
5bugged in version 1.8.0.
6"""
7import os
8from docutils import nodes
9from docutils.frontend import OptionParser
10from sphinx.locale import __
11from sphinx.builders.latex import LaTeXBuilder
12from sphinx.writers.latex import (
13 LaTeXWriter, LaTeXTranslator, rstdim_to_latexdim)
14from sphinx.util import logging
15from sphinx import addnodes
16from sphinx.writers.latex import toRoman, ENUMERATE_LIST_STYLE
17from sphinx.util.docutils import SphinxFileOutput
18from sphinx.util.template import LaTeXRenderer
21class CustomizedSphinxFileOutput(SphinxFileOutput):
22 """Customized FileOutput class for :epkg:`Sphinx`."""
24 def __init__(self, **kwargs):
25 SphinxFileOutput.__init__(self, **kwargs)
27 def write(self, data):
28 res = SphinxFileOutput.write(self, data)
29 return res
32class EnhancedLaTeXTranslator(LaTeXTranslator):
33 """
34 Overwrites `LaTeXTranslator <https://github.com/sphinx-doc/sphinx/blob/master/sphinx/writers/latex.py#L451>`_
35 and modifies a few functions.
36 """
38 def __init__(self, document, builder):
39 if not hasattr(builder, 'config'):
40 raise TypeError(
41 "Unexpected type for builder {0}".format(type(builder)))
42 LaTeXTranslator.__init__(self, document, builder)
44 newlines = builder.config.text_newlines
45 if newlines == 'windows':
46 self.nl = '\r\n'
47 elif newlines == 'native':
48 self.nl = os.linesep
49 else:
50 self.nl = '\n'
52 def add_text(self, line):
53 self.body.append(line)
55 def visit_document(self, node):
56 LaTeXTranslator.visit_document(self, node)
58 def visit_enumerated_list(self, node):
60 def get_enumtype(node):
61 enumtype = node.get('enumtype', 'arabic')
62 if 'alpha' in enumtype and 26 < node.get('start', 0) + len(node): # pylint: disable=C0122
63 # fallback to arabic if alphabet counter overflows
64 enumtype = 'arabic'
66 return enumtype
68 def get_nested_level(node):
69 if node is None:
70 return 0
71 elif isinstance(node, nodes.enumerated_list):
72 return get_nested_level(node.parent) + 1
73 else:
74 return get_nested_level(node.parent)
76 enum = "enum%s" % toRoman(get_nested_level(node)).lower()
77 enumnext = "enum%s" % toRoman(get_nested_level(node) + 1).lower()
78 style = ENUMERATE_LIST_STYLE.get(get_enumtype(node))
80 self.body.append('\\begin{enumerate}\n')
81 self.body.append('\\def\\the%s{%s{%s}}\n' % (enum, style, enum))
82 prefix = node['prefix'] if 'prefix' in node else ''
83 suffix = node['suffix'] if 'suffix' in node else ''
84 self.body.append('\\def\\label%s{%s\\the%s %s}\n' %
85 (enum, prefix, enum, suffix))
86 self.body.append('\\makeatletter\\def\\p@%s{\\p@%s %s\\the%s %s}\\makeatother\n' %
87 (enumnext, enum, prefix, enum, suffix))
88 if 'start' in node:
89 self.body.append('\\setcounter{%s}{%d}\n' %
90 (enum, node['start'] - 1))
91 if self.table:
92 self.table.has_problematic = True
94 def eval_expr(self, expr):
95 md = False
96 rst = True
97 html = False
98 latex = False
99 if not(rst or html or latex or md):
100 raise ValueError("One of them should be True")
101 try:
102 ev = eval(expr)
103 except Exception:
104 raise ValueError(
105 "Unable to interpret expression '{0}'".format(expr))
106 return ev
108 def visit_only(self, node):
109 ev = self.eval_expr(node.attributes['expr'])
110 if ev:
111 pass
112 else:
113 raise nodes.SkipNode
115 def depart_only(self, node):
116 ev = self.eval_expr(node.attributes['expr'])
117 if ev:
118 pass
119 else:
120 # The program should not necessarily be here.
121 pass
123 def latex_image_length(self, width_str, scale=100):
124 try:
125 return rstdim_to_latexdim(width_str, scale)
126 except ValueError:
127 if width_str == 'auto':
128 pass
129 else:
130 self.builder.logger.warning(
131 __('[EnhancedLaTeXTranslator] dimension unit '
132 '%s is invalid. Ignored.'), width_str)
133 return None
134 except TypeError:
135 # Sphinx <= 1.7
136 try:
137 return rstdim_to_latexdim(width_str)
138 except ValueError:
139 if width_str == 'auto':
140 pass
141 else:
142 self.builder.logger.warning(
143 __('[EnhancedLaTeXTranslator] dimension unit '
144 '%s is invalid. Ignored.'), width_str)
145 return None
147 def visit_inheritance_diagram(self, node):
148 pass
150 def depart_inheritance_diagram(self, node):
151 pass
153 def render(self, template_name, variables):
154 renderer = LaTeXRenderer(latex_engine=self.config.latex_engine)
155 if self.builder.config.templates_path is None:
156 tpls = [os.path.join(os.path.abspath(os.path.dirname(__file__)),
157 "templates")]
158 else:
159 tpls = self.builder.config.templates_path
160 for template_dir in tpls:
161 if os.path.exists(template_dir):
162 template = os.path.join(template_dir, template_name)
163 else:
164 template = os.path.join(self.builder.confdir, template_dir,
165 template_name)
166 if os.path.exists(template):
167 return renderer.render(template, variables)
169 return renderer.render(template_name, variables)
171 def visit_imgsgnode(self, node):
172 pass
174 def depart_imgsgnode(self, node):
175 pass
177 def unknown_visit(self, node):
178 logger = logging.getLogger("MdBuilder")
179 logger.warning("[latex] unknown visit node: '{0}' - '{1}'".format(
180 node.__class__.__name__, node))
183class EnhancedLaTeXWriter(LaTeXWriter):
184 """
185 Overwrites `LatexWriter <https://github.com/sphinx-doc/sphinx/blob/master/sphinx/writers/latex.py#L189>`_.
186 """
187 translator_class = EnhancedLaTeXTranslator
189 def __init__(self, builder):
190 if not hasattr(builder, "config"):
191 raise TypeError("Builder has no config: {}".format(type(builder)))
192 LaTeXWriter.__init__(self, builder)
194 def translate(self):
195 visitor = self.builder.create_translator(self.document, self.builder)
196 self.document.walkabout(visitor)
197 self.output = visitor.astext()
200class EnhancedLaTeXBuilder(LaTeXBuilder):
201 """
202 Overwrites `LaTeXBuilder <https://github.com/sphinx-doc/sphinx/blob/master/sphinx/builders/latex/__init__.py>`_.
203 """
204 name = 'elatex'
205 format = 'latex'
206 file_suffix = '.tex'
207 epilog = __('The EnhancedTexinfo files are in %(outdir)s.')
208 if os.name == 'posix':
209 epilog += __("\nRun 'make' in that directory to run these through "
210 "makeinfo\n"
211 "(use 'make info' here to do that automatically).")
213 supported_image_types = ['image/png', 'image/jpeg', 'image/gif']
214 default_translator_class = EnhancedLaTeXTranslator
216 def __init__(self, *args, **kwargs):
217 """
218 Constructor, add a logger.
219 """
220 LaTeXBuilder.__init__(self, *args, **kwargs)
221 self.logger = logging.getLogger("EnhancedLatexBuilder")
222 self._memo_pages = {}
223 self._skip_finish = self.app.config.elatex_bypass_finish
225 def finish(self):
226 if self._skip_finish:
227 return None
228 else:
229 return LaTeXBuilder.finish(self)
231 def get_outfilename(self, pagename):
232 """
233 Overwrites *get_target_uri* to control file names.
234 """
235 return "{0}/{1}.tex".format(self.outdir, pagename).replace("\\", "/")
237 def _get_filename(self, targetname, encoding='utf-8', overwrite_if_changed=True):
238 return CustomizedSphinxFileOutput(destination_path=os.path.join(self.outdir, targetname),
239 encoding=encoding, overwrite_if_changed=overwrite_if_changed)
241 def write(self, *ignored):
242 docwriter = EnhancedLaTeXWriter(self)
243 docsettings = OptionParser(
244 defaults=self.env.settings,
245 components=(docwriter,),
246 read_config_files=True).get_default_values()
248 self.init_document_data()
249 self.write_stylesheet()
251 for entry in self.document_data:
252 docname, targetname, title, author, docclass = entry[:5]
253 toctree_only = False
254 if len(entry) > 5:
255 toctree_only = entry[5]
256 destination = self._get_filename(targetname, encoding='utf-8',
257 overwrite_if_changed=True)
258 self.logger.info(__("processing %s..."), targetname, nonl=1)
259 toctrees = self.env.get_doctree(docname).traverse(addnodes.toctree)
260 if toctrees:
261 if toctrees[0].get('maxdepth') > 0:
262 tocdepth = toctrees[0].get('maxdepth')
263 else:
264 tocdepth = None
265 else:
266 tocdepth = None
267 doctree = self.assemble_doctree(
268 docname, toctree_only,
269 appendices=((docclass != 'howto') and self.config.latex_appendices or []))
270 doctree['tocdepth'] = tocdepth
271 self.post_process_images(doctree)
273 self.logger.info(__("writing... "), nonl=1)
274 doctree.settings = docsettings
275 doctree.settings.author = author
276 doctree.settings.title = title
277 doctree.settings.contentsname = self.get_contentsname(docname)
278 doctree.settings.docname = docname
279 doctree.settings.docclass = docclass
280 docwriter.write(doctree, destination)
281 self.logger.info(
282 "[EnhancedLaTeXBuilder] done in '{}'".format(targetname))
285def setup(app):
286 """
287 Initializes builder @see cl EnhancedLaTeXBuilder.
288 """
289 app.add_config_value('elatex_bypass_finish', False, 'elatex')
290 app.add_builder(EnhancedLaTeXBuilder)