diff -r be27ed110b50 -r d8ac696cc51f buildframework/helium/external/python/lib/common/Sphinx-0.5.1-py2.5.egg/sphinx/latexwriter.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/buildframework/helium/external/python/lib/common/Sphinx-0.5.1-py2.5.egg/sphinx/latexwriter.py Wed Dec 23 19:29:07 2009 +0200 @@ -0,0 +1,1192 @@ +# -*- coding: utf-8 -*- +""" + sphinx.latexwriter + ~~~~~~~~~~~~~~~~~~ + + Custom docutils writer for LaTeX. + + Much of this code is adapted from Dave Kuhlman's "docpy" writer from his + docutils sandbox. + + :copyright: 2007-2008 by Georg Brandl, Dave Kuhlman. + :license: BSD. +""" + +import re +import sys +from os import path + +from docutils import nodes, writers +from docutils.writers.latex2e import Babel + +from sphinx import addnodes +from sphinx import highlighting +from sphinx.locale import admonitionlabels, versionlabels +from sphinx.util import ustrftime +from sphinx.util.texescape import tex_escape_map +from sphinx.util.smartypants import educateQuotesLatex + +HEADER = r'''%% Generated by Sphinx. +\documentclass[%(papersize)s,%(pointsize)s%(classoptions)s]{%(docclass)s} +%(inputenc)s +%(fontenc)s +%(babel)s +%(fontpkg)s +%(fncychap)s +\usepackage{sphinx} +%(preamble)s + +\title{%(title)s} +\date{%(date)s} +\release{%(release)s} +\author{%(author)s} +\newcommand{\sphinxlogo}{%(logo)s} +\renewcommand{\releasename}{%(releasename)s} +%(makeindex)s +%(makemodindex)s +''' + +BEGIN_DOC = r''' +\begin{document} +%(shorthandoff)s +%(maketitle)s +%(tableofcontents)s +''' + +FOOTER = r''' +%(footer)s +\renewcommand{\indexname}{%(modindexname)s} +%(printmodindex)s +\renewcommand{\indexname}{%(indexname)s} +%(printindex)s +\end{document} +''' + + +class LaTeXWriter(writers.Writer): + + supported = ('sphinxlatex',) + + settings_spec = ('LaTeX writer options', '', ( + ('Document name', ['--docname'], {'default': ''}), + ('Document class', ['--docclass'], {'default': 'manual'}), + ('Author', ['--author'], {'default': ''}), + )) + settings_defaults = {} + + output = None + + def __init__(self, builder): + writers.Writer.__init__(self) + self.builder = builder + + def translate(self): + visitor = LaTeXTranslator(self.document, self.builder) + self.document.walkabout(visitor) + self.output = visitor.astext() + + +# Helper classes + +class ExtBabel(Babel): + def get_shorthandoff(self): + shortlang = self.language.split('_')[0] + if shortlang in ('de', 'sl', 'pt', 'es', 'nl', 'pl'): + return '\\shorthandoff{"}' + return '' + + _ISO639_TO_BABEL = Babel._ISO639_TO_BABEL.copy() + _ISO639_TO_BABEL['sl'] = 'slovene' + + +class Table(object): + def __init__(self): + self.col = 0 + self.colcount = 0 + self.colspec = None + self.had_head = False + self.has_verbatim = False + self.caption = None + + +class Desc(object): + def __init__(self, node): + self.env = LaTeXTranslator.desc_map.get(node['desctype'], 'describe') + self.type = self.cls = self.name = self.params = self.annotation = '' + self.count = 0 + + +class LaTeXTranslator(nodes.NodeVisitor): + sectionnames = ["part", "chapter", "section", "subsection", + "subsubsection", "paragraph", "subparagraph"] + + ignore_missing_images = False + + default_elements = { + 'docclass': 'manual', + 'papersize': 'letterpaper', + 'pointsize': '10pt', + 'classoptions': '', + 'inputenc': '\\usepackage[utf8]{inputenc}', + 'fontenc': '\\usepackage[T1]{fontenc}', + 'babel': '\\usepackage{babel}', + 'fontpkg': '\\usepackage{times}', + 'fncychap': '\\usepackage[Bjarne]{fncychap}', + 'preamble': '', + 'title': '', + 'date': '', + 'release': '', + 'author': '', + 'logo': '', + 'releasename': 'Release', + 'makeindex': '\\makeindex', + 'makemodindex': '\\makemodindex', + 'shorthandoff': '', + 'maketitle': '\\maketitle', + 'tableofcontents': '\\tableofcontents', + 'footer': '', + 'printmodindex': '\\printmodindex', + 'printindex': '\\printindex', + } + + def __init__(self, document, builder): + nodes.NodeVisitor.__init__(self, document) + self.builder = builder + self.body = [] + + # sort out some elements + papersize = builder.config.latex_paper_size + 'paper' + if papersize == 'paper': # e.g. command line "-D latex_paper_size=" + papersize = 'letterpaper' + + self.elements = self.default_elements.copy() + self.elements.update({ + 'docclass': document.settings.docclass, + 'papersize': papersize, + 'pointsize': builder.config.latex_font_size, + # if empty, the title is set to the first section title + 'title': document.settings.title, + 'date': ustrftime(builder.config.today_fmt or _('%B %d, %Y')), + 'release': builder.config.release, + 'author': document.settings.author, + 'releasename': _('Release'), + 'preamble': builder.config.latex_preamble, + 'modindexname': _('Module Index'), + 'indexname': _('Index'), + }) + if builder.config.latex_logo: + self.elements['logo'] = '\\includegraphics{%s}\\par' % \ + path.basename(builder.config.latex_logo) + if builder.config.language: + babel = ExtBabel(builder.config.language) + lang = babel.get_language() + if lang: + self.elements['classoptions'] += ',' + babel.get_language() + else: + self.builder.warn('no Babel option known for language %r' % + builder.config.language) + self.elements['shorthandoff'] = babel.get_shorthandoff() + self.elements['fncychap'] = '\\usepackage[Sonny]{fncychap}' + else: + self.elements['classoptions'] += ',english' + if not builder.config.latex_use_modindex: + self.elements['makemodindex'] = '' + self.elements['printmodindex'] = '' + # allow the user to override them all + self.elements.update(builder.config.latex_elements) + + self.highlighter = highlighting.PygmentsBridge( + 'latex', builder.config.pygments_style) + self.context = [] + self.descstack = [] + self.bibitems = [] + self.table = None + self.next_table_colspec = None + self.highlightlang = builder.config.highlight_language + self.highlightlinenothreshold = sys.maxint + self.written_ids = set() + self.footnotestack = [] + if self.elements['docclass'] == 'manual': + if builder.config.latex_use_parts: + self.top_sectionlevel = 0 + else: + self.top_sectionlevel = 1 + else: + self.top_sectionlevel = 2 + self.next_section_target = None + # flags + self.verbatim = None + self.in_title = 0 + self.in_production_list = 0 + self.first_document = 1 + self.this_is_the_title = 1 + self.literal_whitespace = 0 + self.no_contractions = 0 + + def astext(self): + return (HEADER % self.elements + self.highlighter.get_stylesheet() + + u''.join(self.body) + FOOTER % self.elements) + + def visit_document(self, node): + self.footnotestack.append(self.collect_footnotes(node)) + if self.first_document == 1: + # the first document is all the regular content ... + self.body.append(BEGIN_DOC % self.elements) + self.first_document = 0 + elif self.first_document == 0: + # ... and all others are the appendices + self.body.append('\n\\appendix\n') + self.first_document = -1 + # "- 1" because the level is increased before the title is visited + self.sectionlevel = self.top_sectionlevel - 1 + def depart_document(self, node): + if self.bibitems: + widest_label = "" + for bi in self.bibitems: + if len(widest_label) < len(bi[0]): + widest_label = bi[0] + self.body.append('\n\\begin{thebibliography}{%s}\n' % widest_label) + for bi in self.bibitems: + # cite_key: underscores must not be escaped + cite_key = bi[0].replace(r"\_", "_") + self.body.append('\\bibitem[%s]{%s}{%s}\n' % (bi[0], cite_key, bi[1])) + self.body.append('\\end{thebibliography}\n') + self.bibitems = [] + + def visit_start_of_file(self, node): + # This marks the begin of a new file; therefore the current module and + # class must be reset + self.body.append('\n\\resetcurrentobjects\n') + # and also, new footnotes + self.footnotestack.append(self.collect_footnotes(node)) + + def collect_footnotes(self, node): + fnotes = {} + def footnotes_under(n): + if isinstance(n, nodes.footnote): + yield n + else: + for c in n.children: + if isinstance(c, addnodes.start_of_file): + continue + for k in footnotes_under(c): + yield k + for fn in footnotes_under(node): + num = fn.children[0].astext().strip() + fnotes[num] = fn + fn.parent.remove(fn) + return fnotes + + def depart_start_of_file(self, node): + self.footnotestack.pop() + + def visit_highlightlang(self, node): + self.highlightlang = node['lang'] + self.highlightlinenothreshold = node['linenothreshold'] + raise nodes.SkipNode + + def visit_section(self, node): + if not self.this_is_the_title: + self.sectionlevel += 1 + self.body.append('\n\n') + if self.next_section_target: + self.body.append(r'\hypertarget{%s}{}' % self.next_section_target) + self.next_section_target = None + #if node.get('ids'): + # for id in node['ids']: + # if id not in self.written_ids: + # self.body.append(r'\hypertarget{%s}{}' % id) + # self.written_ids.add(id) + def depart_section(self, node): + self.sectionlevel = max(self.sectionlevel - 1, self.top_sectionlevel - 1) + + def visit_problematic(self, node): + self.body.append(r'{\color{red}\bfseries{}') + def depart_problematic(self, node): + self.body.append('}') + + def visit_topic(self, node): + self.body.append('\\setbox0\\vbox{\n' + '\\begin{minipage}{0.95\\textwidth}\n') + def depart_topic(self, node): + self.body.append('\\end{minipage}}\n' + '\\begin{center}\\setlength{\\fboxsep}{5pt}' + '\\shadowbox{\\box0}\\end{center}\n') + visit_sidebar = visit_topic + depart_sidebar = depart_topic + + def visit_glossary(self, node): + pass + def depart_glossary(self, node): + pass + + def visit_productionlist(self, node): + self.body.append('\n\n\\begin{productionlist}\n') + self.in_production_list = 1 + def depart_productionlist(self, node): + self.body.append('\\end{productionlist}\n\n') + self.in_production_list = 0 + + def visit_production(self, node): + if node['tokenname']: + self.body.append('\\production{%s}{' % self.encode(node['tokenname'])) + else: + self.body.append('\\productioncont{') + def depart_production(self, node): + self.body.append('}\n') + + def visit_transition(self, node): + self.body.append('\n\n\\bigskip\\hrule{}\\bigskip\n\n') + def depart_transition(self, node): + pass + + def visit_title(self, node): + parent = node.parent + if isinstance(parent, addnodes.seealso): + # the environment already handles this + raise nodes.SkipNode + elif self.this_is_the_title: + if len(node.children) != 1 and not isinstance(node.children[0], nodes.Text): + self.builder.warn('document title is not a single Text node') + if not self.elements['title']: + # text needs to be escaped since it is inserted into + # the output literally + self.elements['title'] = node.astext().translate(tex_escape_map) + self.this_is_the_title = 0 + raise nodes.SkipNode + elif isinstance(parent, nodes.section): + try: + self.body.append(r'\%s{' % self.sectionnames[self.sectionlevel]) + except IndexError: + from sphinx.application import SphinxError + raise SphinxError('too many nesting section levels for LaTeX, ' + 'at heading: %s' % node.astext()) + self.context.append('}\n') + elif isinstance(parent, (nodes.topic, nodes.sidebar)): + self.body.append(r'\textbf{') + self.context.append('}\n\n\medskip\n\n') + elif isinstance(parent, nodes.Admonition): + self.body.append('{') + self.context.append('}\n') + elif isinstance(parent, nodes.table): + self.table.caption = self.encode(node.astext()) + raise nodes.SkipNode + else: + self.builder.warn('encountered title node not in section, topic, ' + 'table, admonition or sidebar') + self.body.append('\\textbf{') + self.context.append('}\n') + self.in_title = 1 + def depart_title(self, node): + self.in_title = 0 + self.body.append(self.context.pop()) + + def visit_subtitle(self, node): + if isinstance(node.parent, nodes.sidebar): + self.body.append('~\\\\\n\\textbf{') + self.context.append('}\n\\smallskip\n') + else: + self.context.append('') + def depart_subtitle(self, node): + self.body.append(self.context.pop()) + + desc_map = { + 'function' : 'funcdesc', + 'class': 'classdesc', + 'method': 'methoddesc', + 'staticmethod': 'staticmethoddesc', + 'exception': 'excdesc', + 'data': 'datadesc', + 'attribute': 'memberdesc', + 'opcode': 'opcodedesc', + + 'cfunction': 'cfuncdesc', + 'cmember': 'cmemberdesc', + 'cmacro': 'csimplemacrodesc', + 'ctype': 'ctypedesc', + 'cvar': 'cvardesc', + + 'describe': 'describe', + # and all others are 'describe' too + } + + def visit_desc(self, node): + self.descstack.append(Desc(node)) + def depart_desc(self, node): + d = self.descstack.pop() + self.body.append("\\end{%s}\n" % d.env) + + def visit_desc_signature(self, node): + d = self.descstack[-1] + # reset these for every signature + d.type = d.cls = d.name = d.params = '' + def depart_desc_signature(self, node): + d = self.descstack[-1] + d.cls = d.cls.rstrip('.') + if node.parent['desctype'] != 'describe' and node['ids']: + hyper = '\\hypertarget{%s}{}' % node['ids'][0] + else: + hyper = '' + if d.count == 0: + t1 = "\n\n%s\\begin{%s}" % (hyper, d.env) + else: + t1 = "\n%s\\%sline" % (hyper, d.env[:-4]) + d.count += 1 + if d.env in ('funcdesc', 'classdesc', 'excclassdesc'): + t2 = "{%s}{%s}" % (d.name, d.params) + elif d.env in ('datadesc', 'excdesc', 'csimplemacrodesc'): + t2 = "{%s}" % (d.name) + elif d.env in ('methoddesc', 'staticmethoddesc'): + if d.cls: + t2 = "[%s]{%s}{%s}" % (d.cls, d.name, d.params) + else: + t2 = "{%s}{%s}" % (d.name, d.params) + elif d.env == 'memberdesc': + if d.cls: + t2 = "[%s]{%s}" % (d.cls, d.name) + else: + t2 = "{%s}" % d.name + elif d.env == 'cfuncdesc': + if d.cls: + # C++ class names + d.name = '%s::%s' % (d.cls, d.name) + t2 = "{%s}{%s}{%s}" % (d.type, d.name, d.params) + elif d.env == 'cmemberdesc': + try: + type, container = d.type.rsplit(' ', 1) + container = container.rstrip('.') + except ValueError: + container = '' + type = d.type + t2 = "{%s}{%s}{%s}" % (container, type, d.name) + elif d.env == 'cvardesc': + t2 = "{%s}{%s}" % (d.type, d.name) + elif d.env == 'ctypedesc': + t2 = "{%s}" % (d.name) + elif d.env == 'opcodedesc': + t2 = "{%s}{%s}" % (d.name, d.params) + elif d.env == 'describe': + t2 = "{%s}" % d.name + self.body.append(t1 + t2) + + def visit_desc_type(self, node): + d = self.descstack[-1] + if d.env == 'describe': + d.name += self.encode(node.astext()) + else: + self.descstack[-1].type = self.encode(node.astext().strip()) + raise nodes.SkipNode + + def visit_desc_name(self, node): + d = self.descstack[-1] + if d.env == 'describe': + d.name += self.encode(node.astext()) + else: + self.descstack[-1].name = self.encode(node.astext().strip()) + raise nodes.SkipNode + + def visit_desc_addname(self, node): + d = self.descstack[-1] + if d.env == 'describe': + d.name += self.encode(node.astext()) + else: + self.descstack[-1].cls = self.encode(node.astext().strip()) + raise nodes.SkipNode + + def visit_desc_parameterlist(self, node): + d = self.descstack[-1] + if d.env == 'describe': + d.name += self.encode(node.astext()) + else: + self.descstack[-1].params = self.encode(node.astext().strip()) + raise nodes.SkipNode + + def visit_desc_annotation(self, node): + d = self.descstack[-1] + if d.env == 'describe': + d.name += self.encode(node.astext()) + else: + self.descstack[-1].annotation = self.encode(node.astext().strip()) + raise nodes.SkipNode + + def visit_refcount(self, node): + self.body.append("\\emph{") + def depart_refcount(self, node): + self.body.append("}\\\\") + + def visit_desc_content(self, node): + if node.children and not isinstance(node.children[0], nodes.paragraph): + # avoid empty desc environment which causes a formatting bug + self.body.append('~') + def depart_desc_content(self, node): + pass + + def visit_seealso(self, node): + self.body.append("\n\n\\strong{%s:}\n\n" % admonitionlabels['seealso']) + def depart_seealso(self, node): + self.body.append("\n\n") + + def visit_rubric(self, node): + if len(node.children) == 1 and node.children[0].astext() == 'Footnotes': + raise nodes.SkipNode + self.body.append('\\paragraph{') + self.context.append('}\n') + def depart_rubric(self, node): + self.body.append(self.context.pop()) + + def visit_footnote(self, node): + pass + def depart_footnote(self, node): + pass + + def visit_label(self, node): + if isinstance(node.parent, nodes.citation): + self.bibitems[-1][0] = node.astext() + raise nodes.SkipNode + + def visit_tabular_col_spec(self, node): + self.next_table_colspec = node['spec'] + raise nodes.SkipNode + + def visit_table(self, node): + if self.table: + raise NotImplementedError('Nested tables are not supported.') + self.table = Table() + self.tablebody = [] + # Redirect body output until table is finished. + self._body = self.body + self.body = self.tablebody + def depart_table(self, node): + self.body = self._body + if self.table.caption is not None: + self.body.append('\n\\begin{threeparttable}\n' + '\\caption{%s}\n' % self.table.caption) + if self.table.has_verbatim: + self.body.append('\n\\begin{tabular}') + else: + self.body.append('\n\\begin{tabulary}{\\textwidth}') + if self.table.colspec: + self.body.append(self.table.colspec) + else: + if self.table.has_verbatim: + colwidth = 0.95 / self.table.colcount + colspec = ('p{%.3f\\textwidth}|' % colwidth) * self.table.colcount + self.body.append('{|' + colspec + '}\n') + else: + self.body.append('{|' + ('L|' * self.table.colcount) + '}\n') + self.body.extend(self.tablebody) + if self.table.has_verbatim: + self.body.append('\\end{tabular}\n\n') + else: + self.body.append('\\end{tabulary}\n\n') + if self.table.caption is not None: + self.body.append('\\end{threeparttable}\n\n') + self.table = None + self.tablebody = None + + def visit_colspec(self, node): + self.table.colcount += 1 + def depart_colspec(self, node): + pass + + def visit_tgroup(self, node): + pass + def depart_tgroup(self, node): + pass + + def visit_thead(self, node): + if self.next_table_colspec: + self.table.colspec = '{%s}\n' % self.next_table_colspec + self.next_table_colspec = None + self.body.append('\\hline\n') + self.table.had_head = True + def depart_thead(self, node): + self.body.append('\\hline\n') + + def visit_tbody(self, node): + if not self.table.had_head: + self.visit_thead(node) + def depart_tbody(self, node): + self.body.append('\\hline\n') + + def visit_row(self, node): + self.table.col = 0 + def depart_row(self, node): + self.body.append('\\\\\n') + + def visit_entry(self, node): + if node.has_key('morerows') or node.has_key('morecols'): + raise NotImplementedError('Column or row spanning cells are ' + 'not implemented.') + if self.table.col > 0: + self.body.append(' & ') + self.table.col += 1 + if isinstance(node.parent.parent, nodes.thead): + self.body.append('\\textbf{') + self.context.append('}') + else: + self.context.append('') + def depart_entry(self, node): + self.body.append(self.context.pop()) # header + + def visit_acks(self, node): + # this is a list in the source, but should be rendered as a + # comma-separated list here + self.body.append('\n\n') + self.body.append(', '.join(n.astext() for n in node.children[0].children) + '.') + self.body.append('\n\n') + raise nodes.SkipNode + + def visit_bullet_list(self, node): + self.body.append('\\begin{itemize}\n' ) + def depart_bullet_list(self, node): + self.body.append('\\end{itemize}\n' ) + + def visit_enumerated_list(self, node): + self.body.append('\\begin{enumerate}\n' ) + def depart_enumerated_list(self, node): + self.body.append('\\end{enumerate}\n' ) + + def visit_list_item(self, node): + # Append "{}" in case the next character is "[", which would break + # LaTeX's list environment (no numbering and the "[" is not printed). + self.body.append(r'\item {} ') + def depart_list_item(self, node): + self.body.append('\n') + + def visit_definition_list(self, node): + self.body.append('\\begin{description}\n') + def depart_definition_list(self, node): + self.body.append('\\end{description}\n') + + def visit_definition_list_item(self, node): + pass + def depart_definition_list_item(self, node): + pass + + def visit_term(self, node): + ctx = ']' + if node.has_key('ids') and node['ids']: + ctx += '\\hypertarget{%s}{}' % node['ids'][0] + self.body.append('\\item[') + self.context.append(ctx) + def depart_term(self, node): + self.body.append(self.context.pop()) + + def visit_classifier(self, node): + self.body.append('{[}') + def depart_classifier(self, node): + self.body.append('{]}') + + def visit_definition(self, node): + pass + def depart_definition(self, node): + self.body.append('\n') + + def visit_field_list(self, node): + self.body.append('\\begin{quote}\\begin{description}\n') + def depart_field_list(self, node): + self.body.append('\\end{description}\\end{quote}\n') + + def visit_field(self, node): + pass + def depart_field(self, node): + pass + + visit_field_name = visit_term + depart_field_name = depart_term + + visit_field_body = visit_definition + depart_field_body = depart_definition + + def visit_paragraph(self, node): + self.body.append('\n') + def depart_paragraph(self, node): + self.body.append('\n') + + def visit_centered(self, node): + self.body.append('\n\\begin{centering}') + def depart_centered(self, node): + self.body.append('\n\\end{centering}') + + def visit_module(self, node): + modname = node['modname'] + self.body.append('\n\\declaremodule[%s]{}{%s}' % (modname.replace('_', ''), + self.encode(modname))) + self.body.append('\n\\modulesynopsis{%s}' % self.encode(node['synopsis'])) + if node.has_key('platform'): + self.body.append('\\platform{%s}' % self.encode(node['platform'])) + def depart_module(self, node): + pass + + def latex_image_length(self, width_str): + match = re.match('(\d*\.?\d*)\s*(\S*)', width_str) + if not match: + # fallback + return width_str + res = width_str + amount, unit = match.groups()[:2] + if not unit or unit == "px": + # pixels: let LaTeX alone + return None + elif unit == "%": + res = "%.3f\\linewidth" % (float(amount) / 100.0) + return res + + def visit_image(self, node): + attrs = node.attributes + pre = [] # in reverse order + post = [] + include_graphics_options = [] + inline = isinstance(node.parent, nodes.TextElement) + if attrs.has_key('scale'): + # Could also be done with ``scale`` option to + # ``\includegraphics``; doing it this way for consistency. + pre.append('\\scalebox{%f}{' % (attrs['scale'] / 100.0,)) + post.append('}') + if attrs.has_key('width'): + w = self.latex_image_length(attrs['width']) + if w: + include_graphics_options.append('width=%s' % w) + if attrs.has_key('height'): + h = self.latex_image_length(attrs['height']) + if h: + include_graphics_options.append('height=%s' % h) + if attrs.has_key('align'): + align_prepost = { + # By default latex aligns the top of an image. + (1, 'top'): ('', ''), + (1, 'middle'): ('\\raisebox{-0.5\\height}{', '}'), + (1, 'bottom'): ('\\raisebox{-\\height}{', '}'), + (0, 'center'): ('{\\hfill', '\\hfill}'), + # These 2 don't exactly do the right thing. The image should + # be floated alongside the paragraph. See + # http://www.w3.org/TR/html4/struct/objects.html#adef-align-IMG + (0, 'left'): ('{', '\\hfill}'), + (0, 'right'): ('{\\hfill', '}'),} + try: + pre.append(align_prepost[inline, attrs['align']][0]) + post.append(align_prepost[inline, attrs['align']][1]) + except KeyError: + pass # XXX complain here? + if not inline: + pre.append('\n') + post.append('\n') + pre.reverse() + if node['uri'] in self.builder.images: + uri = self.builder.images[node['uri']] + else: + # missing image! + if self.ignore_missing_images: + return + uri = node['uri'] + if uri.find('://') != -1: + # ignore remote images + return + self.body.extend(pre) + options = '' + if include_graphics_options: + options = '[%s]' % ','.join(include_graphics_options) + self.body.append('\\includegraphics%s{%s}' % (options, uri)) + self.body.extend(post) + def depart_image(self, node): + pass + + def visit_figure(self, node): + if (not node.attributes.has_key('align') or + node.attributes['align'] == 'center'): + # centering does not add vertical space like center. + align = '\n\\centering' + align_end = '' + else: + # TODO non vertical space for other alignments. + align = '\\begin{flush%s}' % node.attributes['align'] + align_end = '\\end{flush%s}' % node.attributes['align'] + self.body.append('\\begin{figure}[htbp]%s\n' % align) + self.context.append('%s\\end{figure}\n' % align_end) + def depart_figure(self, node): + self.body.append(self.context.pop()) + + def visit_caption(self, node): + self.body.append('\\caption{') + def depart_caption(self, node): + self.body.append('}') + + def visit_legend(self, node): + self.body.append('{\\small ') + def depart_legend(self, node): + self.body.append('}') + + def visit_admonition(self, node): + self.body.append('\n\\begin{notice}{note}') + def depart_admonition(self, node): + self.body.append('\\end{notice}\n') + + def _make_visit_admonition(name): + def visit_admonition(self, node): + self.body.append('\n\\begin{notice}{%s}{%s:}' % + (name, admonitionlabels[name])) + return visit_admonition + def _depart_named_admonition(self, node): + self.body.append('\\end{notice}\n') + + visit_attention = _make_visit_admonition('attention') + depart_attention = _depart_named_admonition + visit_caution = _make_visit_admonition('caution') + depart_caution = _depart_named_admonition + visit_danger = _make_visit_admonition('danger') + depart_danger = _depart_named_admonition + visit_error = _make_visit_admonition('error') + depart_error = _depart_named_admonition + visit_hint = _make_visit_admonition('hint') + depart_hint = _depart_named_admonition + visit_important = _make_visit_admonition('important') + depart_important = _depart_named_admonition + visit_note = _make_visit_admonition('note') + depart_note = _depart_named_admonition + visit_tip = _make_visit_admonition('tip') + depart_tip = _depart_named_admonition + visit_warning = _make_visit_admonition('warning') + depart_warning = _depart_named_admonition + + def visit_versionmodified(self, node): + intro = versionlabels[node['type']] % node['version'] + if node.children: + intro += ': ' + else: + intro += '.' + self.body.append(intro) + def depart_versionmodified(self, node): + pass + + def visit_target(self, node): + def add_target(id): + # indexing uses standard LaTeX index markup, so the targets + # will be generated differently + if not id.startswith('index-'): + self.body.append(r'\hypertarget{%s}{}' % id) + + if node.has_key('refid') and node['refid'] not in self.written_ids: + parindex = node.parent.index(node) + try: + next = node.parent[parindex+1] + if isinstance(next, nodes.section): + self.next_section_target = node['refid'] + return + except IndexError: + pass + add_target(node['refid']) + self.written_ids.add(node['refid']) + def depart_target(self, node): + pass + + def visit_attribution(self, node): + self.body.append('\n\\begin{flushright}\n') + self.body.append('---') + def depart_attribution(self, node): + self.body.append('\n\\end{flushright}\n') + + def visit_index(self, node, scre=re.compile(r';\s*')): + entries = node['entries'] + for type, string, tid, _ in entries: + if type == 'single': + self.body.append(r'\index{%s}' % scre.sub('!', self.encode(string))) + elif type == 'pair': + parts = tuple(self.encode(x.strip()) for x in string.split(';', 1)) + try: + self.body.append(r'\indexii{%s}{%s}' % parts) + except TypeError: + self.builder.warn('invalid pair index entry %r' % string) + elif type == 'triple': + parts = tuple(self.encode(x.strip()) for x in string.split(';', 2)) + try: + self.body.append(r'\indexiii{%s}{%s}{%s}' % parts) + except TypeError: + self.builder.warn('invalid triple index entry %r' % string) + else: + self.builder.warn('unknown index entry type %s found' % type) + raise nodes.SkipNode + + def visit_raw(self, node): + if 'latex' in node.get('format', '').split(): + self.body.append(node.astext()) + raise nodes.SkipNode + + def visit_reference(self, node): + uri = node.get('refuri', '') + if self.in_title or not uri: + self.context.append('') + elif uri.startswith('mailto:') or uri.startswith('http:') or \ + uri.startswith('https:') or uri.startswith('ftp:'): + self.body.append('\\href{%s}{' % self.encode(uri)) + self.context.append('}') + elif uri.startswith('#'): + self.body.append('\\hyperlink{%s}{' % uri[1:]) + self.context.append('}') + elif uri.startswith('@token'): + if self.in_production_list: + self.body.append('\\token{') + else: + self.body.append('\\grammartoken{') + self.context.append('}') + else: + self.builder.warn('unusable reference target found: %s' % uri) + self.context.append('') + def depart_reference(self, node): + self.body.append(self.context.pop()) + + def visit_pending_xref(self, node): + pass + def depart_pending_xref(self, node): + pass + + def visit_emphasis(self, node): + self.body.append(r'\emph{') + def depart_emphasis(self, node): + self.body.append('}') + + def visit_literal_emphasis(self, node): + self.body.append(r'\emph{\texttt{') + self.no_contractions += 1 + def depart_literal_emphasis(self, node): + self.body.append('}}') + self.no_contractions -= 1 + + def visit_strong(self, node): + self.body.append(r'\textbf{') + def depart_strong(self, node): + self.body.append('}') + + def visit_title_reference(self, node): + self.body.append(r'\emph{') + def depart_title_reference(self, node): + self.body.append('}') + + def visit_citation(self, node): + # TODO maybe use cite bibitems + self.bibitems.append(['', '']) + self.context.append(len(self.body)) + def depart_citation(self, node): + size = self.context.pop() + text = ''.join(self.body[size:]) + del self.body[size:] + self.bibitems[-1][1] = text + + def visit_citation_reference(self, node): + citeid = node.astext() + self.body.append('\\cite{%s}' % citeid) + raise nodes.SkipNode + + def visit_literal(self, node): + content = self.encode(node.astext().strip()) + if self.in_title: + self.body.append(r'\texttt{%s}' % content) + elif node.has_key('role') and node['role'] == 'samp': + self.body.append(r'\samp{%s}' % content) + else: + self.body.append(r'\code{%s}' % content) + raise nodes.SkipNode + + def visit_footnote_reference(self, node): + num = node.astext().strip() + try: + fn = self.footnotestack[-1][num] + except (KeyError, IndexError): + raise nodes.SkipNode + self.body.append('\\footnote{') + fn.walkabout(self) + raise nodes.SkipChildren + def depart_footnote_reference(self, node): + self.body.append('}') + + def visit_literal_block(self, node): + self.verbatim = '' + def depart_literal_block(self, node): + code = self.verbatim.rstrip('\n') + lang = self.highlightlang + linenos = code.count('\n') >= self.highlightlinenothreshold - 1 + if node.has_key('language'): + # code-block directives + lang = node['language'] + if node.has_key('linenos'): + linenos = node['linenos'] + hlcode = self.highlighter.highlight_block(code, lang, linenos) + # workaround for Unicode issue + hlcode = hlcode.replace(u'€', u'@texteuro[]') + # must use original Verbatim environment and "tabular" environment + if self.table: + hlcode = hlcode.replace('\\begin{Verbatim}', + '\\begin{OriginalVerbatim}') + self.table.has_verbatim = True + # get consistent trailer + hlcode = hlcode.rstrip()[:-14] # strip \end{Verbatim} + hlcode = hlcode.rstrip() + '\n' + self.body.append('\n' + hlcode + '\\end{%sVerbatim}\n' % + (self.table and 'Original' or '')) + self.verbatim = None + visit_doctest_block = visit_literal_block + depart_doctest_block = depart_literal_block + + def visit_line_block(self, node): + """line-block: + * whitespace (including linebreaks) is significant + * inline markup is supported. + * serif typeface + """ + self.body.append('{\\raggedright{}') + self.literal_whitespace = 1 + def depart_line_block(self, node): + self.literal_whitespace = 0 + # remove the last \\ + del self.body[-1] + self.body.append('}\n') + + def visit_line(self, node): + self._line_start = len(self.body) + def depart_line(self, node): + if self._line_start == len(self.body): + # no output in this line -- add a nonbreaking space, else the + # \\ command will give an error + self.body.append('~') + if self.table is not None: + self.body.append('\\newline\n') + else: + self.body.append('\\\\\n') + + def visit_block_quote(self, node): + # If the block quote contains a single object and that object + # is a list, then generate a list not a block quote. + # This lets us indent lists. + done = 0 + if len(node.children) == 1: + child = node.children[0] + if isinstance(child, nodes.bullet_list) or \ + isinstance(child, nodes.enumerated_list): + done = 1 + if not done: + self.body.append('\\begin{quote}\n') + def depart_block_quote(self, node): + done = 0 + if len(node.children) == 1: + child = node.children[0] + if isinstance(child, nodes.bullet_list) or \ + isinstance(child, nodes.enumerated_list): + done = 1 + if not done: + self.body.append('\\end{quote}\n') + + # option node handling copied from docutils' latex writer + + def visit_option(self, node): + if self.context[-1]: + # this is not the first option + self.body.append(', ') + def depart_option(self, node): + # flag that the first option is done. + self.context[-1] += 1 + + def visit_option_argument(self, node): + """The delimiter betweeen an option and its argument.""" + self.body.append(node.get('delimiter', ' ')) + def depart_option_argument(self, node): + pass + + def visit_option_group(self, node): + self.body.append('\\item [') + # flag for first option + self.context.append(0) + def depart_option_group(self, node): + self.context.pop() # the flag + self.body.append('] ') + + def visit_option_list(self, node): + self.body.append('\\begin{optionlist}{3cm}\n') + def depart_option_list(self, node): + self.body.append('\\end{optionlist}\n') + + def visit_option_list_item(self, node): + pass + def depart_option_list_item(self, node): + pass + + def visit_option_string(self, node): + ostring = node.astext() + self.body.append(self.encode(ostring.replace('--', u'-{-}'))) + raise nodes.SkipNode + + def visit_description(self, node): + self.body.append( ' ' ) + def depart_description(self, node): + pass + + def visit_superscript(self, node): + self.body.append('$^{\\text{') + def depart_superscript(self, node): + self.body.append('}}$') + + def visit_subscript(self, node): + self.body.append('$_{\\text{') + def depart_subscript(self, node): + self.body.append('}}$') + + def visit_substitution_definition(self, node): + raise nodes.SkipNode + + def visit_substitution_reference(self, node): + raise nodes.SkipNode + + def visit_generated(self, node): + pass + def depart_generated(self, node): + pass + + def visit_compound(self, node): + pass + def depart_compound(self, node): + pass + + def visit_container(self, node): + pass + def depart_container(self, node): + pass + + def visit_decoration(self, node): + pass + def depart_decoration(self, node): + pass + + # text handling + + def encode(self, text): + text = unicode(text).translate(tex_escape_map) + if self.literal_whitespace: + # Insert a blank before the newline, to avoid + # ! LaTeX Error: There's no line here to end. + text = text.replace(u'\n', u'~\\\\\n').replace(u' ', u'~') + if self.no_contractions: + text = text.replace('--', u'-{-}') + return text + + def visit_Text(self, node): + if self.verbatim is not None: + self.verbatim += node.astext() + else: + text = self.encode(node.astext()) + self.body.append(educateQuotesLatex(text)) + def depart_Text(self, node): + pass + + def visit_comment(self, node): + raise nodes.SkipNode + + def visit_meta(self, node): + # only valid for HTML + raise nodes.SkipNode + + def visit_system_message(self, node): + pass + def depart_system_message(self, node): + self.body.append('\n') + + def unknown_visit(self, node): + raise NotImplementedError('Unknown node: ' + node.__class__.__name__)