buildframework/helium/external/python/lib/common/Sphinx-0.5.1-py2.5.egg/sphinx/latexwriter.py
changeset 179 d8ac696cc51f
equal deleted inserted replaced
1:be27ed110b50 179:d8ac696cc51f
       
     1 # -*- coding: utf-8 -*-
       
     2 """
       
     3     sphinx.latexwriter
       
     4     ~~~~~~~~~~~~~~~~~~
       
     5 
       
     6     Custom docutils writer for LaTeX.
       
     7 
       
     8     Much of this code is adapted from Dave Kuhlman's "docpy" writer from his
       
     9     docutils sandbox.
       
    10 
       
    11     :copyright: 2007-2008 by Georg Brandl, Dave Kuhlman.
       
    12     :license: BSD.
       
    13 """
       
    14 
       
    15 import re
       
    16 import sys
       
    17 from os import path
       
    18 
       
    19 from docutils import nodes, writers
       
    20 from docutils.writers.latex2e import Babel
       
    21 
       
    22 from sphinx import addnodes
       
    23 from sphinx import highlighting
       
    24 from sphinx.locale import admonitionlabels, versionlabels
       
    25 from sphinx.util import ustrftime
       
    26 from sphinx.util.texescape import tex_escape_map
       
    27 from sphinx.util.smartypants import educateQuotesLatex
       
    28 
       
    29 HEADER = r'''%% Generated by Sphinx.
       
    30 \documentclass[%(papersize)s,%(pointsize)s%(classoptions)s]{%(docclass)s}
       
    31 %(inputenc)s
       
    32 %(fontenc)s
       
    33 %(babel)s
       
    34 %(fontpkg)s
       
    35 %(fncychap)s
       
    36 \usepackage{sphinx}
       
    37 %(preamble)s
       
    38 
       
    39 \title{%(title)s}
       
    40 \date{%(date)s}
       
    41 \release{%(release)s}
       
    42 \author{%(author)s}
       
    43 \newcommand{\sphinxlogo}{%(logo)s}
       
    44 \renewcommand{\releasename}{%(releasename)s}
       
    45 %(makeindex)s
       
    46 %(makemodindex)s
       
    47 '''
       
    48 
       
    49 BEGIN_DOC = r'''
       
    50 \begin{document}
       
    51 %(shorthandoff)s
       
    52 %(maketitle)s
       
    53 %(tableofcontents)s
       
    54 '''
       
    55 
       
    56 FOOTER = r'''
       
    57 %(footer)s
       
    58 \renewcommand{\indexname}{%(modindexname)s}
       
    59 %(printmodindex)s
       
    60 \renewcommand{\indexname}{%(indexname)s}
       
    61 %(printindex)s
       
    62 \end{document}
       
    63 '''
       
    64 
       
    65 
       
    66 class LaTeXWriter(writers.Writer):
       
    67 
       
    68     supported = ('sphinxlatex',)
       
    69 
       
    70     settings_spec = ('LaTeX writer options', '', (
       
    71         ('Document name', ['--docname'], {'default': ''}),
       
    72         ('Document class', ['--docclass'], {'default': 'manual'}),
       
    73         ('Author', ['--author'], {'default': ''}),
       
    74         ))
       
    75     settings_defaults = {}
       
    76 
       
    77     output = None
       
    78 
       
    79     def __init__(self, builder):
       
    80         writers.Writer.__init__(self)
       
    81         self.builder = builder
       
    82 
       
    83     def translate(self):
       
    84         visitor = LaTeXTranslator(self.document, self.builder)
       
    85         self.document.walkabout(visitor)
       
    86         self.output = visitor.astext()
       
    87 
       
    88 
       
    89 # Helper classes
       
    90 
       
    91 class ExtBabel(Babel):
       
    92     def get_shorthandoff(self):
       
    93         shortlang = self.language.split('_')[0]
       
    94         if shortlang in ('de', 'sl', 'pt', 'es', 'nl', 'pl'):
       
    95             return '\\shorthandoff{"}'
       
    96         return ''
       
    97 
       
    98     _ISO639_TO_BABEL = Babel._ISO639_TO_BABEL.copy()
       
    99     _ISO639_TO_BABEL['sl'] = 'slovene'
       
   100 
       
   101 
       
   102 class Table(object):
       
   103     def __init__(self):
       
   104         self.col = 0
       
   105         self.colcount = 0
       
   106         self.colspec = None
       
   107         self.had_head = False
       
   108         self.has_verbatim = False
       
   109         self.caption = None
       
   110 
       
   111 
       
   112 class Desc(object):
       
   113     def __init__(self, node):
       
   114         self.env = LaTeXTranslator.desc_map.get(node['desctype'], 'describe')
       
   115         self.type = self.cls = self.name = self.params = self.annotation = ''
       
   116         self.count = 0
       
   117 
       
   118 
       
   119 class LaTeXTranslator(nodes.NodeVisitor):
       
   120     sectionnames = ["part", "chapter", "section", "subsection",
       
   121                     "subsubsection", "paragraph", "subparagraph"]
       
   122 
       
   123     ignore_missing_images = False
       
   124 
       
   125     default_elements = {
       
   126         'docclass':        'manual',
       
   127         'papersize':       'letterpaper',
       
   128         'pointsize':       '10pt',
       
   129         'classoptions':    '',
       
   130         'inputenc':        '\\usepackage[utf8]{inputenc}',
       
   131         'fontenc':         '\\usepackage[T1]{fontenc}',
       
   132         'babel':           '\\usepackage{babel}',
       
   133         'fontpkg':         '\\usepackage{times}',
       
   134         'fncychap':        '\\usepackage[Bjarne]{fncychap}',
       
   135         'preamble':        '',
       
   136         'title':           '',
       
   137         'date':            '',
       
   138         'release':         '',
       
   139         'author':          '',
       
   140         'logo':            '',
       
   141         'releasename':     'Release',
       
   142         'makeindex':       '\\makeindex',
       
   143         'makemodindex':    '\\makemodindex',
       
   144         'shorthandoff':    '',
       
   145         'maketitle':       '\\maketitle',
       
   146         'tableofcontents': '\\tableofcontents',
       
   147         'footer':          '',
       
   148         'printmodindex':   '\\printmodindex',
       
   149         'printindex':      '\\printindex',
       
   150     }
       
   151 
       
   152     def __init__(self, document, builder):
       
   153         nodes.NodeVisitor.__init__(self, document)
       
   154         self.builder = builder
       
   155         self.body = []
       
   156 
       
   157         # sort out some elements
       
   158         papersize = builder.config.latex_paper_size + 'paper'
       
   159         if papersize == 'paper': # e.g. command line "-D latex_paper_size="
       
   160             papersize = 'letterpaper'
       
   161 
       
   162         self.elements = self.default_elements.copy()
       
   163         self.elements.update({
       
   164             'docclass':     document.settings.docclass,
       
   165             'papersize':    papersize,
       
   166             'pointsize':    builder.config.latex_font_size,
       
   167             # if empty, the title is set to the first section title
       
   168             'title':        document.settings.title,
       
   169             'date':         ustrftime(builder.config.today_fmt or _('%B %d, %Y')),
       
   170             'release':      builder.config.release,
       
   171             'author':       document.settings.author,
       
   172             'releasename':  _('Release'),
       
   173             'preamble':     builder.config.latex_preamble,
       
   174             'modindexname': _('Module Index'),
       
   175             'indexname':    _('Index'),
       
   176         })
       
   177         if builder.config.latex_logo:
       
   178             self.elements['logo'] = '\\includegraphics{%s}\\par' % \
       
   179                                     path.basename(builder.config.latex_logo)
       
   180         if builder.config.language:
       
   181             babel = ExtBabel(builder.config.language)
       
   182             lang = babel.get_language()
       
   183             if lang:
       
   184                 self.elements['classoptions'] += ',' + babel.get_language()
       
   185             else:
       
   186                 self.builder.warn('no Babel option known for language %r' %
       
   187                                   builder.config.language)
       
   188             self.elements['shorthandoff'] = babel.get_shorthandoff()
       
   189             self.elements['fncychap'] = '\\usepackage[Sonny]{fncychap}'
       
   190         else:
       
   191             self.elements['classoptions'] += ',english'
       
   192         if not builder.config.latex_use_modindex:
       
   193             self.elements['makemodindex'] = ''
       
   194             self.elements['printmodindex'] = ''
       
   195         # allow the user to override them all
       
   196         self.elements.update(builder.config.latex_elements)
       
   197 
       
   198         self.highlighter = highlighting.PygmentsBridge(
       
   199             'latex', builder.config.pygments_style)
       
   200         self.context = []
       
   201         self.descstack = []
       
   202         self.bibitems = []
       
   203         self.table = None
       
   204         self.next_table_colspec = None
       
   205         self.highlightlang = builder.config.highlight_language
       
   206         self.highlightlinenothreshold = sys.maxint
       
   207         self.written_ids = set()
       
   208         self.footnotestack = []
       
   209         if self.elements['docclass'] == 'manual':
       
   210             if builder.config.latex_use_parts:
       
   211                 self.top_sectionlevel = 0
       
   212             else:
       
   213                 self.top_sectionlevel = 1
       
   214         else:
       
   215             self.top_sectionlevel = 2
       
   216         self.next_section_target = None
       
   217         # flags
       
   218         self.verbatim = None
       
   219         self.in_title = 0
       
   220         self.in_production_list = 0
       
   221         self.first_document = 1
       
   222         self.this_is_the_title = 1
       
   223         self.literal_whitespace = 0
       
   224         self.no_contractions = 0
       
   225 
       
   226     def astext(self):
       
   227         return (HEADER % self.elements + self.highlighter.get_stylesheet() +
       
   228                 u''.join(self.body) + FOOTER % self.elements)
       
   229 
       
   230     def visit_document(self, node):
       
   231         self.footnotestack.append(self.collect_footnotes(node))
       
   232         if self.first_document == 1:
       
   233             # the first document is all the regular content ...
       
   234             self.body.append(BEGIN_DOC % self.elements)
       
   235             self.first_document = 0
       
   236         elif self.first_document == 0:
       
   237             # ... and all others are the appendices
       
   238             self.body.append('\n\\appendix\n')
       
   239             self.first_document = -1
       
   240         # "- 1" because the level is increased before the title is visited
       
   241         self.sectionlevel = self.top_sectionlevel - 1
       
   242     def depart_document(self, node):
       
   243         if self.bibitems:
       
   244             widest_label = ""
       
   245             for bi in self.bibitems:
       
   246                 if len(widest_label) < len(bi[0]):
       
   247                     widest_label = bi[0]
       
   248             self.body.append('\n\\begin{thebibliography}{%s}\n' % widest_label)
       
   249             for bi in self.bibitems:
       
   250                 # cite_key: underscores must not be escaped
       
   251                 cite_key = bi[0].replace(r"\_", "_")
       
   252                 self.body.append('\\bibitem[%s]{%s}{%s}\n' % (bi[0], cite_key, bi[1]))
       
   253             self.body.append('\\end{thebibliography}\n')
       
   254             self.bibitems = []
       
   255 
       
   256     def visit_start_of_file(self, node):
       
   257         # This marks the begin of a new file; therefore the current module and
       
   258         # class must be reset
       
   259         self.body.append('\n\\resetcurrentobjects\n')
       
   260         # and also, new footnotes
       
   261         self.footnotestack.append(self.collect_footnotes(node))
       
   262 
       
   263     def collect_footnotes(self, node):
       
   264         fnotes = {}
       
   265         def footnotes_under(n):
       
   266             if isinstance(n, nodes.footnote):
       
   267                 yield n
       
   268             else:
       
   269                 for c in n.children:
       
   270                     if isinstance(c, addnodes.start_of_file):
       
   271                         continue
       
   272                     for k in footnotes_under(c):
       
   273                         yield k
       
   274         for fn in footnotes_under(node):
       
   275             num = fn.children[0].astext().strip()
       
   276             fnotes[num] = fn
       
   277             fn.parent.remove(fn)
       
   278         return fnotes
       
   279 
       
   280     def depart_start_of_file(self, node):
       
   281         self.footnotestack.pop()
       
   282 
       
   283     def visit_highlightlang(self, node):
       
   284         self.highlightlang = node['lang']
       
   285         self.highlightlinenothreshold = node['linenothreshold']
       
   286         raise nodes.SkipNode
       
   287 
       
   288     def visit_section(self, node):
       
   289         if not self.this_is_the_title:
       
   290             self.sectionlevel += 1
       
   291         self.body.append('\n\n')
       
   292         if self.next_section_target:
       
   293             self.body.append(r'\hypertarget{%s}{}' % self.next_section_target)
       
   294             self.next_section_target = None
       
   295         #if node.get('ids'):
       
   296         #    for id in node['ids']:
       
   297         #        if id not in self.written_ids:
       
   298         #            self.body.append(r'\hypertarget{%s}{}' % id)
       
   299         #            self.written_ids.add(id)
       
   300     def depart_section(self, node):
       
   301         self.sectionlevel = max(self.sectionlevel - 1, self.top_sectionlevel - 1)
       
   302 
       
   303     def visit_problematic(self, node):
       
   304         self.body.append(r'{\color{red}\bfseries{}')
       
   305     def depart_problematic(self, node):
       
   306         self.body.append('}')
       
   307 
       
   308     def visit_topic(self, node):
       
   309         self.body.append('\\setbox0\\vbox{\n'
       
   310                          '\\begin{minipage}{0.95\\textwidth}\n')
       
   311     def depart_topic(self, node):
       
   312         self.body.append('\\end{minipage}}\n'
       
   313                          '\\begin{center}\\setlength{\\fboxsep}{5pt}'
       
   314                          '\\shadowbox{\\box0}\\end{center}\n')
       
   315     visit_sidebar = visit_topic
       
   316     depart_sidebar = depart_topic
       
   317 
       
   318     def visit_glossary(self, node):
       
   319         pass
       
   320     def depart_glossary(self, node):
       
   321         pass
       
   322 
       
   323     def visit_productionlist(self, node):
       
   324         self.body.append('\n\n\\begin{productionlist}\n')
       
   325         self.in_production_list = 1
       
   326     def depart_productionlist(self, node):
       
   327         self.body.append('\\end{productionlist}\n\n')
       
   328         self.in_production_list = 0
       
   329 
       
   330     def visit_production(self, node):
       
   331         if node['tokenname']:
       
   332             self.body.append('\\production{%s}{' % self.encode(node['tokenname']))
       
   333         else:
       
   334             self.body.append('\\productioncont{')
       
   335     def depart_production(self, node):
       
   336         self.body.append('}\n')
       
   337 
       
   338     def visit_transition(self, node):
       
   339         self.body.append('\n\n\\bigskip\\hrule{}\\bigskip\n\n')
       
   340     def depart_transition(self, node):
       
   341         pass
       
   342 
       
   343     def visit_title(self, node):
       
   344         parent = node.parent
       
   345         if isinstance(parent, addnodes.seealso):
       
   346             # the environment already handles this
       
   347             raise nodes.SkipNode
       
   348         elif self.this_is_the_title:
       
   349             if len(node.children) != 1 and not isinstance(node.children[0], nodes.Text):
       
   350                 self.builder.warn('document title is not a single Text node')
       
   351             if not self.elements['title']:
       
   352                 # text needs to be escaped since it is inserted into
       
   353                 # the output literally
       
   354                 self.elements['title'] = node.astext().translate(tex_escape_map)
       
   355             self.this_is_the_title = 0
       
   356             raise nodes.SkipNode
       
   357         elif isinstance(parent, nodes.section):
       
   358             try:
       
   359                 self.body.append(r'\%s{' % self.sectionnames[self.sectionlevel])
       
   360             except IndexError:
       
   361                 from sphinx.application import SphinxError
       
   362                 raise SphinxError('too many nesting section levels for LaTeX, '
       
   363                                   'at heading: %s' % node.astext())
       
   364             self.context.append('}\n')
       
   365         elif isinstance(parent, (nodes.topic, nodes.sidebar)):
       
   366             self.body.append(r'\textbf{')
       
   367             self.context.append('}\n\n\medskip\n\n')
       
   368         elif isinstance(parent, nodes.Admonition):
       
   369             self.body.append('{')
       
   370             self.context.append('}\n')
       
   371         elif isinstance(parent, nodes.table):
       
   372             self.table.caption = self.encode(node.astext())
       
   373             raise nodes.SkipNode
       
   374         else:
       
   375             self.builder.warn('encountered title node not in section, topic, '
       
   376                               'table, admonition or sidebar')
       
   377             self.body.append('\\textbf{')
       
   378             self.context.append('}\n')
       
   379         self.in_title = 1
       
   380     def depart_title(self, node):
       
   381         self.in_title = 0
       
   382         self.body.append(self.context.pop())
       
   383 
       
   384     def visit_subtitle(self, node):
       
   385         if isinstance(node.parent, nodes.sidebar):
       
   386             self.body.append('~\\\\\n\\textbf{')
       
   387             self.context.append('}\n\\smallskip\n')
       
   388         else:
       
   389             self.context.append('')
       
   390     def depart_subtitle(self, node):
       
   391         self.body.append(self.context.pop())
       
   392 
       
   393     desc_map = {
       
   394         'function' : 'funcdesc',
       
   395         'class': 'classdesc',
       
   396         'method': 'methoddesc',
       
   397         'staticmethod': 'staticmethoddesc',
       
   398         'exception': 'excdesc',
       
   399         'data': 'datadesc',
       
   400         'attribute': 'memberdesc',
       
   401         'opcode': 'opcodedesc',
       
   402 
       
   403         'cfunction': 'cfuncdesc',
       
   404         'cmember': 'cmemberdesc',
       
   405         'cmacro': 'csimplemacrodesc',
       
   406         'ctype': 'ctypedesc',
       
   407         'cvar': 'cvardesc',
       
   408 
       
   409         'describe': 'describe',
       
   410         # and all others are 'describe' too
       
   411     }
       
   412 
       
   413     def visit_desc(self, node):
       
   414         self.descstack.append(Desc(node))
       
   415     def depart_desc(self, node):
       
   416         d = self.descstack.pop()
       
   417         self.body.append("\\end{%s}\n" % d.env)
       
   418 
       
   419     def visit_desc_signature(self, node):
       
   420         d = self.descstack[-1]
       
   421         # reset these for every signature
       
   422         d.type = d.cls = d.name = d.params = ''
       
   423     def depart_desc_signature(self, node):
       
   424         d = self.descstack[-1]
       
   425         d.cls = d.cls.rstrip('.')
       
   426         if node.parent['desctype'] != 'describe' and node['ids']:
       
   427             hyper = '\\hypertarget{%s}{}' % node['ids'][0]
       
   428         else:
       
   429             hyper = ''
       
   430         if d.count == 0:
       
   431             t1 = "\n\n%s\\begin{%s}" % (hyper, d.env)
       
   432         else:
       
   433             t1 = "\n%s\\%sline" % (hyper, d.env[:-4])
       
   434         d.count += 1
       
   435         if d.env in ('funcdesc', 'classdesc', 'excclassdesc'):
       
   436             t2 = "{%s}{%s}" % (d.name, d.params)
       
   437         elif d.env in ('datadesc', 'excdesc', 'csimplemacrodesc'):
       
   438             t2 = "{%s}" % (d.name)
       
   439         elif d.env in ('methoddesc', 'staticmethoddesc'):
       
   440             if d.cls:
       
   441                 t2 = "[%s]{%s}{%s}" % (d.cls, d.name, d.params)
       
   442             else:
       
   443                 t2 = "{%s}{%s}" % (d.name, d.params)
       
   444         elif d.env == 'memberdesc':
       
   445             if d.cls:
       
   446                 t2 = "[%s]{%s}" % (d.cls, d.name)
       
   447             else:
       
   448                 t2 = "{%s}" % d.name
       
   449         elif d.env == 'cfuncdesc':
       
   450             if d.cls:
       
   451                 # C++ class names
       
   452                 d.name = '%s::%s' % (d.cls, d.name)
       
   453             t2 = "{%s}{%s}{%s}" % (d.type, d.name, d.params)
       
   454         elif d.env == 'cmemberdesc':
       
   455             try:
       
   456                 type, container = d.type.rsplit(' ', 1)
       
   457                 container = container.rstrip('.')
       
   458             except ValueError:
       
   459                 container = ''
       
   460                 type = d.type
       
   461             t2 = "{%s}{%s}{%s}" % (container, type, d.name)
       
   462         elif d.env == 'cvardesc':
       
   463             t2 = "{%s}{%s}" % (d.type, d.name)
       
   464         elif d.env == 'ctypedesc':
       
   465             t2 = "{%s}" % (d.name)
       
   466         elif d.env == 'opcodedesc':
       
   467             t2 = "{%s}{%s}" % (d.name, d.params)
       
   468         elif d.env == 'describe':
       
   469             t2 = "{%s}" % d.name
       
   470         self.body.append(t1 + t2)
       
   471 
       
   472     def visit_desc_type(self, node):
       
   473         d = self.descstack[-1]
       
   474         if d.env == 'describe':
       
   475             d.name += self.encode(node.astext())
       
   476         else:
       
   477             self.descstack[-1].type = self.encode(node.astext().strip())
       
   478         raise nodes.SkipNode
       
   479 
       
   480     def visit_desc_name(self, node):
       
   481         d = self.descstack[-1]
       
   482         if d.env == 'describe':
       
   483             d.name += self.encode(node.astext())
       
   484         else:
       
   485             self.descstack[-1].name = self.encode(node.astext().strip())
       
   486         raise nodes.SkipNode
       
   487 
       
   488     def visit_desc_addname(self, node):
       
   489         d = self.descstack[-1]
       
   490         if d.env == 'describe':
       
   491             d.name += self.encode(node.astext())
       
   492         else:
       
   493             self.descstack[-1].cls = self.encode(node.astext().strip())
       
   494         raise nodes.SkipNode
       
   495 
       
   496     def visit_desc_parameterlist(self, node):
       
   497         d = self.descstack[-1]
       
   498         if d.env == 'describe':
       
   499             d.name += self.encode(node.astext())
       
   500         else:
       
   501             self.descstack[-1].params = self.encode(node.astext().strip())
       
   502         raise nodes.SkipNode
       
   503 
       
   504     def visit_desc_annotation(self, node):
       
   505         d = self.descstack[-1]
       
   506         if d.env == 'describe':
       
   507             d.name += self.encode(node.astext())
       
   508         else:
       
   509             self.descstack[-1].annotation = self.encode(node.astext().strip())
       
   510         raise nodes.SkipNode
       
   511 
       
   512     def visit_refcount(self, node):
       
   513         self.body.append("\\emph{")
       
   514     def depart_refcount(self, node):
       
   515         self.body.append("}\\\\")
       
   516 
       
   517     def visit_desc_content(self, node):
       
   518         if node.children and not isinstance(node.children[0], nodes.paragraph):
       
   519             # avoid empty desc environment which causes a formatting bug
       
   520             self.body.append('~')
       
   521     def depart_desc_content(self, node):
       
   522         pass
       
   523 
       
   524     def visit_seealso(self, node):
       
   525         self.body.append("\n\n\\strong{%s:}\n\n" % admonitionlabels['seealso'])
       
   526     def depart_seealso(self, node):
       
   527         self.body.append("\n\n")
       
   528 
       
   529     def visit_rubric(self, node):
       
   530         if len(node.children) == 1 and node.children[0].astext() == 'Footnotes':
       
   531             raise nodes.SkipNode
       
   532         self.body.append('\\paragraph{')
       
   533         self.context.append('}\n')
       
   534     def depart_rubric(self, node):
       
   535         self.body.append(self.context.pop())
       
   536 
       
   537     def visit_footnote(self, node):
       
   538         pass
       
   539     def depart_footnote(self, node):
       
   540         pass
       
   541 
       
   542     def visit_label(self, node):
       
   543         if isinstance(node.parent, nodes.citation):
       
   544             self.bibitems[-1][0] = node.astext()
       
   545         raise nodes.SkipNode
       
   546 
       
   547     def visit_tabular_col_spec(self, node):
       
   548         self.next_table_colspec = node['spec']
       
   549         raise nodes.SkipNode
       
   550 
       
   551     def visit_table(self, node):
       
   552         if self.table:
       
   553             raise NotImplementedError('Nested tables are not supported.')
       
   554         self.table = Table()
       
   555         self.tablebody = []
       
   556         # Redirect body output until table is finished.
       
   557         self._body = self.body
       
   558         self.body = self.tablebody
       
   559     def depart_table(self, node):
       
   560         self.body = self._body
       
   561         if self.table.caption is not None:
       
   562             self.body.append('\n\\begin{threeparttable}\n'
       
   563                              '\\caption{%s}\n' % self.table.caption)
       
   564         if self.table.has_verbatim:
       
   565             self.body.append('\n\\begin{tabular}')
       
   566         else:
       
   567             self.body.append('\n\\begin{tabulary}{\\textwidth}')
       
   568         if self.table.colspec:
       
   569             self.body.append(self.table.colspec)
       
   570         else:
       
   571             if self.table.has_verbatim:
       
   572                 colwidth = 0.95 / self.table.colcount
       
   573                 colspec = ('p{%.3f\\textwidth}|' % colwidth) * self.table.colcount
       
   574                 self.body.append('{|' + colspec + '}\n')
       
   575             else:
       
   576                 self.body.append('{|' + ('L|' * self.table.colcount) + '}\n')
       
   577         self.body.extend(self.tablebody)
       
   578         if self.table.has_verbatim:
       
   579             self.body.append('\\end{tabular}\n\n')
       
   580         else:
       
   581             self.body.append('\\end{tabulary}\n\n')
       
   582         if self.table.caption is not None:
       
   583             self.body.append('\\end{threeparttable}\n\n')
       
   584         self.table = None
       
   585         self.tablebody = None
       
   586 
       
   587     def visit_colspec(self, node):
       
   588         self.table.colcount += 1
       
   589     def depart_colspec(self, node):
       
   590         pass
       
   591 
       
   592     def visit_tgroup(self, node):
       
   593         pass
       
   594     def depart_tgroup(self, node):
       
   595         pass
       
   596 
       
   597     def visit_thead(self, node):
       
   598         if self.next_table_colspec:
       
   599             self.table.colspec = '{%s}\n' % self.next_table_colspec
       
   600         self.next_table_colspec = None
       
   601         self.body.append('\\hline\n')
       
   602         self.table.had_head = True
       
   603     def depart_thead(self, node):
       
   604         self.body.append('\\hline\n')
       
   605 
       
   606     def visit_tbody(self, node):
       
   607         if not self.table.had_head:
       
   608             self.visit_thead(node)
       
   609     def depart_tbody(self, node):
       
   610         self.body.append('\\hline\n')
       
   611 
       
   612     def visit_row(self, node):
       
   613         self.table.col = 0
       
   614     def depart_row(self, node):
       
   615         self.body.append('\\\\\n')
       
   616 
       
   617     def visit_entry(self, node):
       
   618         if node.has_key('morerows') or node.has_key('morecols'):
       
   619             raise NotImplementedError('Column or row spanning cells are '
       
   620                                       'not implemented.')
       
   621         if self.table.col > 0:
       
   622             self.body.append(' & ')
       
   623         self.table.col += 1
       
   624         if isinstance(node.parent.parent, nodes.thead):
       
   625             self.body.append('\\textbf{')
       
   626             self.context.append('}')
       
   627         else:
       
   628             self.context.append('')
       
   629     def depart_entry(self, node):
       
   630         self.body.append(self.context.pop()) # header
       
   631 
       
   632     def visit_acks(self, node):
       
   633         # this is a list in the source, but should be rendered as a
       
   634         # comma-separated list here
       
   635         self.body.append('\n\n')
       
   636         self.body.append(', '.join(n.astext() for n in node.children[0].children) + '.')
       
   637         self.body.append('\n\n')
       
   638         raise nodes.SkipNode
       
   639 
       
   640     def visit_bullet_list(self, node):
       
   641         self.body.append('\\begin{itemize}\n' )
       
   642     def depart_bullet_list(self, node):
       
   643         self.body.append('\\end{itemize}\n' )
       
   644 
       
   645     def visit_enumerated_list(self, node):
       
   646         self.body.append('\\begin{enumerate}\n' )
       
   647     def depart_enumerated_list(self, node):
       
   648         self.body.append('\\end{enumerate}\n' )
       
   649 
       
   650     def visit_list_item(self, node):
       
   651         # Append "{}" in case the next character is "[", which would break
       
   652         # LaTeX's list environment (no numbering and the "[" is not printed).
       
   653         self.body.append(r'\item {} ')
       
   654     def depart_list_item(self, node):
       
   655         self.body.append('\n')
       
   656 
       
   657     def visit_definition_list(self, node):
       
   658         self.body.append('\\begin{description}\n')
       
   659     def depart_definition_list(self, node):
       
   660         self.body.append('\\end{description}\n')
       
   661 
       
   662     def visit_definition_list_item(self, node):
       
   663         pass
       
   664     def depart_definition_list_item(self, node):
       
   665         pass
       
   666 
       
   667     def visit_term(self, node):
       
   668         ctx = ']'
       
   669         if node.has_key('ids') and node['ids']:
       
   670             ctx += '\\hypertarget{%s}{}' % node['ids'][0]
       
   671         self.body.append('\\item[')
       
   672         self.context.append(ctx)
       
   673     def depart_term(self, node):
       
   674         self.body.append(self.context.pop())
       
   675 
       
   676     def visit_classifier(self, node):
       
   677         self.body.append('{[}')
       
   678     def depart_classifier(self, node):
       
   679         self.body.append('{]}')
       
   680 
       
   681     def visit_definition(self, node):
       
   682         pass
       
   683     def depart_definition(self, node):
       
   684         self.body.append('\n')
       
   685 
       
   686     def visit_field_list(self, node):
       
   687         self.body.append('\\begin{quote}\\begin{description}\n')
       
   688     def depart_field_list(self, node):
       
   689         self.body.append('\\end{description}\\end{quote}\n')
       
   690 
       
   691     def visit_field(self, node):
       
   692         pass
       
   693     def depart_field(self, node):
       
   694         pass
       
   695 
       
   696     visit_field_name = visit_term
       
   697     depart_field_name = depart_term
       
   698 
       
   699     visit_field_body = visit_definition
       
   700     depart_field_body = depart_definition
       
   701 
       
   702     def visit_paragraph(self, node):
       
   703         self.body.append('\n')
       
   704     def depart_paragraph(self, node):
       
   705         self.body.append('\n')
       
   706 
       
   707     def visit_centered(self, node):
       
   708         self.body.append('\n\\begin{centering}')
       
   709     def depart_centered(self, node):
       
   710         self.body.append('\n\\end{centering}')
       
   711 
       
   712     def visit_module(self, node):
       
   713         modname = node['modname']
       
   714         self.body.append('\n\\declaremodule[%s]{}{%s}' % (modname.replace('_', ''),
       
   715                                                           self.encode(modname)))
       
   716         self.body.append('\n\\modulesynopsis{%s}' % self.encode(node['synopsis']))
       
   717         if node.has_key('platform'):
       
   718             self.body.append('\\platform{%s}' % self.encode(node['platform']))
       
   719     def depart_module(self, node):
       
   720         pass
       
   721 
       
   722     def latex_image_length(self, width_str):
       
   723         match = re.match('(\d*\.?\d*)\s*(\S*)', width_str)
       
   724         if not match:
       
   725             # fallback
       
   726             return width_str
       
   727         res = width_str
       
   728         amount, unit = match.groups()[:2]
       
   729         if not unit or unit == "px":
       
   730             # pixels: let LaTeX alone
       
   731             return None
       
   732         elif unit == "%":
       
   733             res = "%.3f\\linewidth" % (float(amount) / 100.0)
       
   734         return res
       
   735 
       
   736     def visit_image(self, node):
       
   737         attrs = node.attributes
       
   738         pre = []                        # in reverse order
       
   739         post = []
       
   740         include_graphics_options = []
       
   741         inline = isinstance(node.parent, nodes.TextElement)
       
   742         if attrs.has_key('scale'):
       
   743             # Could also be done with ``scale`` option to
       
   744             # ``\includegraphics``; doing it this way for consistency.
       
   745             pre.append('\\scalebox{%f}{' % (attrs['scale'] / 100.0,))
       
   746             post.append('}')
       
   747         if attrs.has_key('width'):
       
   748             w = self.latex_image_length(attrs['width'])
       
   749             if w:
       
   750                 include_graphics_options.append('width=%s' % w)
       
   751         if attrs.has_key('height'):
       
   752             h = self.latex_image_length(attrs['height'])
       
   753             if h:
       
   754                 include_graphics_options.append('height=%s' % h)
       
   755         if attrs.has_key('align'):
       
   756             align_prepost = {
       
   757                 # By default latex aligns the top of an image.
       
   758                 (1, 'top'): ('', ''),
       
   759                 (1, 'middle'): ('\\raisebox{-0.5\\height}{', '}'),
       
   760                 (1, 'bottom'): ('\\raisebox{-\\height}{', '}'),
       
   761                 (0, 'center'): ('{\\hfill', '\\hfill}'),
       
   762                 # These 2 don't exactly do the right thing.  The image should
       
   763                 # be floated alongside the paragraph.  See
       
   764                 # http://www.w3.org/TR/html4/struct/objects.html#adef-align-IMG
       
   765                 (0, 'left'): ('{', '\\hfill}'),
       
   766                 (0, 'right'): ('{\\hfill', '}'),}
       
   767             try:
       
   768                 pre.append(align_prepost[inline, attrs['align']][0])
       
   769                 post.append(align_prepost[inline, attrs['align']][1])
       
   770             except KeyError:
       
   771                 pass                    # XXX complain here?
       
   772         if not inline:
       
   773             pre.append('\n')
       
   774             post.append('\n')
       
   775         pre.reverse()
       
   776         if node['uri'] in self.builder.images:
       
   777             uri = self.builder.images[node['uri']]
       
   778         else:
       
   779             # missing image!
       
   780             if self.ignore_missing_images:
       
   781                 return
       
   782             uri = node['uri']
       
   783         if uri.find('://') != -1:
       
   784             # ignore remote images
       
   785             return
       
   786         self.body.extend(pre)
       
   787         options = ''
       
   788         if include_graphics_options:
       
   789             options = '[%s]' % ','.join(include_graphics_options)
       
   790         self.body.append('\\includegraphics%s{%s}' % (options, uri))
       
   791         self.body.extend(post)
       
   792     def depart_image(self, node):
       
   793         pass
       
   794 
       
   795     def visit_figure(self, node):
       
   796         if (not node.attributes.has_key('align') or
       
   797             node.attributes['align'] == 'center'):
       
   798             # centering does not add vertical space like center.
       
   799             align = '\n\\centering'
       
   800             align_end = ''
       
   801         else:
       
   802             # TODO non vertical space for other alignments.
       
   803             align = '\\begin{flush%s}' % node.attributes['align']
       
   804             align_end = '\\end{flush%s}' % node.attributes['align']
       
   805         self.body.append('\\begin{figure}[htbp]%s\n' % align)
       
   806         self.context.append('%s\\end{figure}\n' % align_end)
       
   807     def depart_figure(self, node):
       
   808         self.body.append(self.context.pop())
       
   809 
       
   810     def visit_caption(self, node):
       
   811         self.body.append('\\caption{')
       
   812     def depart_caption(self, node):
       
   813         self.body.append('}')
       
   814 
       
   815     def visit_legend(self, node):
       
   816         self.body.append('{\\small ')
       
   817     def depart_legend(self, node):
       
   818         self.body.append('}')
       
   819 
       
   820     def visit_admonition(self, node):
       
   821         self.body.append('\n\\begin{notice}{note}')
       
   822     def depart_admonition(self, node):
       
   823         self.body.append('\\end{notice}\n')
       
   824 
       
   825     def _make_visit_admonition(name):
       
   826         def visit_admonition(self, node):
       
   827             self.body.append('\n\\begin{notice}{%s}{%s:}' %
       
   828                              (name, admonitionlabels[name]))
       
   829         return visit_admonition
       
   830     def _depart_named_admonition(self, node):
       
   831         self.body.append('\\end{notice}\n')
       
   832 
       
   833     visit_attention = _make_visit_admonition('attention')
       
   834     depart_attention = _depart_named_admonition
       
   835     visit_caution = _make_visit_admonition('caution')
       
   836     depart_caution = _depart_named_admonition
       
   837     visit_danger = _make_visit_admonition('danger')
       
   838     depart_danger = _depart_named_admonition
       
   839     visit_error = _make_visit_admonition('error')
       
   840     depart_error = _depart_named_admonition
       
   841     visit_hint = _make_visit_admonition('hint')
       
   842     depart_hint = _depart_named_admonition
       
   843     visit_important = _make_visit_admonition('important')
       
   844     depart_important = _depart_named_admonition
       
   845     visit_note = _make_visit_admonition('note')
       
   846     depart_note = _depart_named_admonition
       
   847     visit_tip = _make_visit_admonition('tip')
       
   848     depart_tip = _depart_named_admonition
       
   849     visit_warning = _make_visit_admonition('warning')
       
   850     depart_warning = _depart_named_admonition
       
   851 
       
   852     def visit_versionmodified(self, node):
       
   853         intro = versionlabels[node['type']] % node['version']
       
   854         if node.children:
       
   855             intro += ': '
       
   856         else:
       
   857             intro += '.'
       
   858         self.body.append(intro)
       
   859     def depart_versionmodified(self, node):
       
   860         pass
       
   861 
       
   862     def visit_target(self, node):
       
   863         def add_target(id):
       
   864             # indexing uses standard LaTeX index markup, so the targets
       
   865             # will be generated differently
       
   866             if not id.startswith('index-'):
       
   867                 self.body.append(r'\hypertarget{%s}{}' % id)
       
   868 
       
   869         if node.has_key('refid') and node['refid'] not in self.written_ids:
       
   870             parindex = node.parent.index(node)
       
   871             try:
       
   872                 next = node.parent[parindex+1]
       
   873                 if isinstance(next, nodes.section):
       
   874                     self.next_section_target = node['refid']
       
   875                     return
       
   876             except IndexError:
       
   877                 pass
       
   878             add_target(node['refid'])
       
   879             self.written_ids.add(node['refid'])
       
   880     def depart_target(self, node):
       
   881         pass
       
   882 
       
   883     def visit_attribution(self, node):
       
   884         self.body.append('\n\\begin{flushright}\n')
       
   885         self.body.append('---')
       
   886     def depart_attribution(self, node):
       
   887         self.body.append('\n\\end{flushright}\n')
       
   888 
       
   889     def visit_index(self, node, scre=re.compile(r';\s*')):
       
   890         entries = node['entries']
       
   891         for type, string, tid, _ in entries:
       
   892             if type == 'single':
       
   893                 self.body.append(r'\index{%s}' % scre.sub('!', self.encode(string)))
       
   894             elif type == 'pair':
       
   895                 parts = tuple(self.encode(x.strip()) for x in string.split(';', 1))
       
   896                 try:
       
   897                     self.body.append(r'\indexii{%s}{%s}' % parts)
       
   898                 except TypeError:
       
   899                     self.builder.warn('invalid pair index entry %r' % string)
       
   900             elif type == 'triple':
       
   901                 parts = tuple(self.encode(x.strip()) for x in string.split(';', 2))
       
   902                 try:
       
   903                     self.body.append(r'\indexiii{%s}{%s}{%s}' % parts)
       
   904                 except TypeError:
       
   905                     self.builder.warn('invalid triple index entry %r' % string)
       
   906             else:
       
   907                 self.builder.warn('unknown index entry type %s found' % type)
       
   908         raise nodes.SkipNode
       
   909 
       
   910     def visit_raw(self, node):
       
   911         if 'latex' in node.get('format', '').split():
       
   912             self.body.append(node.astext())
       
   913         raise nodes.SkipNode
       
   914 
       
   915     def visit_reference(self, node):
       
   916         uri = node.get('refuri', '')
       
   917         if self.in_title or not uri:
       
   918             self.context.append('')
       
   919         elif uri.startswith('mailto:') or uri.startswith('http:') or \
       
   920              uri.startswith('https:') or uri.startswith('ftp:'):
       
   921             self.body.append('\\href{%s}{' % self.encode(uri))
       
   922             self.context.append('}')
       
   923         elif uri.startswith('#'):
       
   924             self.body.append('\\hyperlink{%s}{' % uri[1:])
       
   925             self.context.append('}')
       
   926         elif uri.startswith('@token'):
       
   927             if self.in_production_list:
       
   928                 self.body.append('\\token{')
       
   929             else:
       
   930                 self.body.append('\\grammartoken{')
       
   931             self.context.append('}')
       
   932         else:
       
   933             self.builder.warn('unusable reference target found: %s' % uri)
       
   934             self.context.append('')
       
   935     def depart_reference(self, node):
       
   936         self.body.append(self.context.pop())
       
   937 
       
   938     def visit_pending_xref(self, node):
       
   939         pass
       
   940     def depart_pending_xref(self, node):
       
   941         pass
       
   942 
       
   943     def visit_emphasis(self, node):
       
   944         self.body.append(r'\emph{')
       
   945     def depart_emphasis(self, node):
       
   946         self.body.append('}')
       
   947 
       
   948     def visit_literal_emphasis(self, node):
       
   949         self.body.append(r'\emph{\texttt{')
       
   950         self.no_contractions += 1
       
   951     def depart_literal_emphasis(self, node):
       
   952         self.body.append('}}')
       
   953         self.no_contractions -= 1
       
   954 
       
   955     def visit_strong(self, node):
       
   956         self.body.append(r'\textbf{')
       
   957     def depart_strong(self, node):
       
   958         self.body.append('}')
       
   959 
       
   960     def visit_title_reference(self, node):
       
   961         self.body.append(r'\emph{')
       
   962     def depart_title_reference(self, node):
       
   963         self.body.append('}')
       
   964 
       
   965     def visit_citation(self, node):
       
   966         # TODO maybe use cite bibitems
       
   967         self.bibitems.append(['', ''])
       
   968         self.context.append(len(self.body))
       
   969     def depart_citation(self, node):
       
   970         size = self.context.pop()
       
   971         text = ''.join(self.body[size:])
       
   972         del self.body[size:]
       
   973         self.bibitems[-1][1] = text
       
   974 
       
   975     def visit_citation_reference(self, node):
       
   976         citeid = node.astext()
       
   977         self.body.append('\\cite{%s}' % citeid)
       
   978         raise nodes.SkipNode
       
   979 
       
   980     def visit_literal(self, node):
       
   981         content = self.encode(node.astext().strip())
       
   982         if self.in_title:
       
   983             self.body.append(r'\texttt{%s}' % content)
       
   984         elif node.has_key('role') and node['role'] == 'samp':
       
   985             self.body.append(r'\samp{%s}' % content)
       
   986         else:
       
   987             self.body.append(r'\code{%s}' % content)
       
   988         raise nodes.SkipNode
       
   989 
       
   990     def visit_footnote_reference(self, node):
       
   991         num = node.astext().strip()
       
   992         try:
       
   993             fn = self.footnotestack[-1][num]
       
   994         except (KeyError, IndexError):
       
   995             raise nodes.SkipNode
       
   996         self.body.append('\\footnote{')
       
   997         fn.walkabout(self)
       
   998         raise nodes.SkipChildren
       
   999     def depart_footnote_reference(self, node):
       
  1000         self.body.append('}')
       
  1001 
       
  1002     def visit_literal_block(self, node):
       
  1003         self.verbatim = ''
       
  1004     def depart_literal_block(self, node):
       
  1005         code = self.verbatim.rstrip('\n')
       
  1006         lang = self.highlightlang
       
  1007         linenos = code.count('\n') >= self.highlightlinenothreshold - 1
       
  1008         if node.has_key('language'):
       
  1009             # code-block directives
       
  1010             lang = node['language']
       
  1011         if node.has_key('linenos'):
       
  1012             linenos = node['linenos']
       
  1013         hlcode = self.highlighter.highlight_block(code, lang, linenos)
       
  1014         # workaround for Unicode issue
       
  1015         hlcode = hlcode.replace(u'€', u'@texteuro[]')
       
  1016         # must use original Verbatim environment and "tabular" environment
       
  1017         if self.table:
       
  1018             hlcode = hlcode.replace('\\begin{Verbatim}',
       
  1019                                     '\\begin{OriginalVerbatim}')
       
  1020             self.table.has_verbatim = True
       
  1021         # get consistent trailer
       
  1022         hlcode = hlcode.rstrip()[:-14] # strip \end{Verbatim}
       
  1023         hlcode = hlcode.rstrip() + '\n'
       
  1024         self.body.append('\n' + hlcode + '\\end{%sVerbatim}\n' %
       
  1025                          (self.table and 'Original' or ''))
       
  1026         self.verbatim = None
       
  1027     visit_doctest_block = visit_literal_block
       
  1028     depart_doctest_block = depart_literal_block
       
  1029 
       
  1030     def visit_line_block(self, node):
       
  1031         """line-block:
       
  1032         * whitespace (including linebreaks) is significant
       
  1033         * inline markup is supported.
       
  1034         * serif typeface
       
  1035         """
       
  1036         self.body.append('{\\raggedright{}')
       
  1037         self.literal_whitespace = 1
       
  1038     def depart_line_block(self, node):
       
  1039         self.literal_whitespace = 0
       
  1040         # remove the last \\
       
  1041         del self.body[-1]
       
  1042         self.body.append('}\n')
       
  1043 
       
  1044     def visit_line(self, node):
       
  1045         self._line_start = len(self.body)
       
  1046     def depart_line(self, node):
       
  1047         if self._line_start == len(self.body):
       
  1048             # no output in this line -- add a nonbreaking space, else the
       
  1049             # \\ command will give an error
       
  1050             self.body.append('~')
       
  1051         if self.table is not None:
       
  1052             self.body.append('\\newline\n')
       
  1053         else:
       
  1054             self.body.append('\\\\\n')
       
  1055 
       
  1056     def visit_block_quote(self, node):
       
  1057         # If the block quote contains a single object and that object
       
  1058         # is a list, then generate a list not a block quote.
       
  1059         # This lets us indent lists.
       
  1060         done = 0
       
  1061         if len(node.children) == 1:
       
  1062             child = node.children[0]
       
  1063             if isinstance(child, nodes.bullet_list) or \
       
  1064                     isinstance(child, nodes.enumerated_list):
       
  1065                 done = 1
       
  1066         if not done:
       
  1067             self.body.append('\\begin{quote}\n')
       
  1068     def depart_block_quote(self, node):
       
  1069         done = 0
       
  1070         if len(node.children) == 1:
       
  1071             child = node.children[0]
       
  1072             if isinstance(child, nodes.bullet_list) or \
       
  1073                     isinstance(child, nodes.enumerated_list):
       
  1074                 done = 1
       
  1075         if not done:
       
  1076             self.body.append('\\end{quote}\n')
       
  1077 
       
  1078     # option node handling copied from docutils' latex writer
       
  1079 
       
  1080     def visit_option(self, node):
       
  1081         if self.context[-1]:
       
  1082             # this is not the first option
       
  1083             self.body.append(', ')
       
  1084     def depart_option(self, node):
       
  1085         # flag that the first option is done.
       
  1086         self.context[-1] += 1
       
  1087 
       
  1088     def visit_option_argument(self, node):
       
  1089         """The delimiter betweeen an option and its argument."""
       
  1090         self.body.append(node.get('delimiter', ' '))
       
  1091     def depart_option_argument(self, node):
       
  1092         pass
       
  1093 
       
  1094     def visit_option_group(self, node):
       
  1095         self.body.append('\\item [')
       
  1096         # flag for first option
       
  1097         self.context.append(0)
       
  1098     def depart_option_group(self, node):
       
  1099         self.context.pop() # the flag
       
  1100         self.body.append('] ')
       
  1101 
       
  1102     def visit_option_list(self, node):
       
  1103         self.body.append('\\begin{optionlist}{3cm}\n')
       
  1104     def depart_option_list(self, node):
       
  1105         self.body.append('\\end{optionlist}\n')
       
  1106 
       
  1107     def visit_option_list_item(self, node):
       
  1108         pass
       
  1109     def depart_option_list_item(self, node):
       
  1110         pass
       
  1111 
       
  1112     def visit_option_string(self, node):
       
  1113         ostring = node.astext()
       
  1114         self.body.append(self.encode(ostring.replace('--', u'-{-}')))
       
  1115         raise nodes.SkipNode
       
  1116 
       
  1117     def visit_description(self, node):
       
  1118         self.body.append( ' ' )
       
  1119     def depart_description(self, node):
       
  1120         pass
       
  1121 
       
  1122     def visit_superscript(self, node):
       
  1123         self.body.append('$^{\\text{')
       
  1124     def depart_superscript(self, node):
       
  1125         self.body.append('}}$')
       
  1126 
       
  1127     def visit_subscript(self, node):
       
  1128         self.body.append('$_{\\text{')
       
  1129     def depart_subscript(self, node):
       
  1130         self.body.append('}}$')
       
  1131 
       
  1132     def visit_substitution_definition(self, node):
       
  1133         raise nodes.SkipNode
       
  1134 
       
  1135     def visit_substitution_reference(self, node):
       
  1136         raise nodes.SkipNode
       
  1137 
       
  1138     def visit_generated(self, node):
       
  1139         pass
       
  1140     def depart_generated(self, node):
       
  1141         pass
       
  1142 
       
  1143     def visit_compound(self, node):
       
  1144         pass
       
  1145     def depart_compound(self, node):
       
  1146         pass
       
  1147 
       
  1148     def visit_container(self, node):
       
  1149         pass
       
  1150     def depart_container(self, node):
       
  1151         pass
       
  1152 
       
  1153     def visit_decoration(self, node):
       
  1154         pass
       
  1155     def depart_decoration(self, node):
       
  1156         pass
       
  1157 
       
  1158     # text handling
       
  1159 
       
  1160     def encode(self, text):
       
  1161         text = unicode(text).translate(tex_escape_map)
       
  1162         if self.literal_whitespace:
       
  1163             # Insert a blank before the newline, to avoid
       
  1164             # ! LaTeX Error: There's no line here to end.
       
  1165             text = text.replace(u'\n', u'~\\\\\n').replace(u' ', u'~')
       
  1166         if self.no_contractions:
       
  1167             text = text.replace('--', u'-{-}')
       
  1168         return text
       
  1169 
       
  1170     def visit_Text(self, node):
       
  1171         if self.verbatim is not None:
       
  1172             self.verbatim += node.astext()
       
  1173         else:
       
  1174             text = self.encode(node.astext())
       
  1175             self.body.append(educateQuotesLatex(text))
       
  1176     def depart_Text(self, node):
       
  1177         pass
       
  1178 
       
  1179     def visit_comment(self, node):
       
  1180         raise nodes.SkipNode
       
  1181 
       
  1182     def visit_meta(self, node):
       
  1183         # only valid for HTML
       
  1184         raise nodes.SkipNode
       
  1185 
       
  1186     def visit_system_message(self, node):
       
  1187         pass
       
  1188     def depart_system_message(self, node):
       
  1189         self.body.append('\n')
       
  1190 
       
  1191     def unknown_visit(self, node):
       
  1192         raise NotImplementedError('Unknown node: ' + node.__class__.__name__)