2007-12-05 15:11:36 -06:00
|
|
|
# -*- 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 by Georg Brandl, Dave Kuhlman.
|
|
|
|
:license: Python license.
|
|
|
|
"""
|
|
|
|
|
|
|
|
import re
|
|
|
|
import time
|
2007-12-05 20:00:01 -06:00
|
|
|
import string
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
from docutils import frontend, nodes, languages, writers, utils
|
|
|
|
|
2007-12-05 20:00:01 -06:00
|
|
|
from . import addnodes
|
2007-12-06 04:00:57 -06:00
|
|
|
from . import highlighting
|
2007-12-05 20:00:01 -06:00
|
|
|
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
HEADER = r'''%% Generated by Sphinx.
|
2007-12-05 20:00:01 -06:00
|
|
|
\documentclass[%(papersize)s,%(pointsize)s]{%(docclass)s}
|
2007-12-07 14:27:52 -06:00
|
|
|
\usepackage[utf8]{inputenc}
|
2007-12-05 20:00:01 -06:00
|
|
|
\usepackage[colorlinks]{hyperref}
|
2007-12-05 15:11:36 -06:00
|
|
|
\title{%(title)s}
|
|
|
|
\date{%(date)s}
|
|
|
|
\release{%(release)s}
|
2007-12-05 20:00:01 -06:00
|
|
|
\author{Guido van Rossum\\ %% XXX
|
|
|
|
Fred L. Drake, Jr., editor} %% XXX
|
2007-12-05 15:11:36 -06:00
|
|
|
\authoraddress{
|
|
|
|
\strong{Python Software Foundation}\\
|
|
|
|
Email: \email{docs@python.org}
|
|
|
|
}
|
|
|
|
\makeindex
|
|
|
|
|
|
|
|
'''
|
|
|
|
|
2007-12-05 20:00:01 -06:00
|
|
|
FOOTER = r'''
|
2007-12-05 15:11:36 -06:00
|
|
|
\printindex
|
|
|
|
\end{document}
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
class LaTeXWriter(writers.Writer):
|
|
|
|
|
|
|
|
supported = ('sphinxlatex',)
|
|
|
|
|
|
|
|
settings_spec = ('No options here.', '', ())
|
|
|
|
settings_defaults = {}
|
|
|
|
|
|
|
|
output = None
|
|
|
|
|
|
|
|
def __init__(self, config, buildername):
|
|
|
|
writers.Writer.__init__(self)
|
|
|
|
self.config = config
|
|
|
|
|
|
|
|
def translate(self):
|
|
|
|
try:
|
|
|
|
visitor = LaTeXTranslator(self.document, self.config)
|
|
|
|
self.document.walkabout(visitor)
|
|
|
|
self.output = visitor.astext()
|
|
|
|
except:
|
|
|
|
import pdb, sys, traceback
|
|
|
|
traceback.print_exc()
|
|
|
|
tb = sys.exc_info()[2]
|
|
|
|
pdb.post_mortem(tb)
|
|
|
|
|
|
|
|
|
2007-12-05 20:00:01 -06:00
|
|
|
# Helper classes
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
class TableSpec:
|
|
|
|
def __init__(self):
|
|
|
|
self.columnCount = 0
|
2007-12-05 20:00:01 -06:00
|
|
|
self.firstRow = 1
|
|
|
|
|
|
|
|
class Desc:
|
|
|
|
def __init__(self, node):
|
|
|
|
self.env = LaTeXTranslator.desc_map[node['desctype']]
|
|
|
|
self.ni = node['noindex']
|
|
|
|
self.type = self.cls = self.name = self.params = ''
|
|
|
|
self.count = 0
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
|
|
|
|
class LaTeXTranslator(nodes.NodeVisitor):
|
|
|
|
sectionnames = ["chapter", "chapter", "section", "subsection",
|
|
|
|
"subsubsection", "paragraph", "subparagraph"]
|
|
|
|
|
|
|
|
def __init__(self, document, config):
|
|
|
|
nodes.NodeVisitor.__init__(self, document)
|
|
|
|
self.body = []
|
2007-12-07 14:27:52 -06:00
|
|
|
docclass = document.settings.docclass
|
|
|
|
self.options = {'docclass': docclass,
|
2007-12-05 20:00:01 -06:00
|
|
|
'papersize': 'a4paper', # XXX
|
2007-12-05 15:11:36 -06:00
|
|
|
'pointsize': '12pt',
|
|
|
|
'filename': document.settings.filename,
|
2007-12-16 06:20:41 -06:00
|
|
|
'title': None, # is determined later
|
2007-12-05 15:11:36 -06:00
|
|
|
'release': config['release'],
|
|
|
|
'date': time.strftime(config.get('today_fmt', '%B %d, %Y')),
|
|
|
|
}
|
|
|
|
self.context = []
|
2007-12-05 20:00:01 -06:00
|
|
|
self.descstack = []
|
|
|
|
self.highlightlang = 'python'
|
|
|
|
self.written_ids = set()
|
2007-12-07 14:27:52 -06:00
|
|
|
self.top_sectionlevel = 0 if docclass == 'manual' else 1
|
2007-12-05 20:00:01 -06:00
|
|
|
# flags
|
2007-12-06 04:00:57 -06:00
|
|
|
self.verbatim = None
|
2007-12-05 20:00:01 -06:00
|
|
|
self.in_title = 0
|
2007-12-16 06:20:41 -06:00
|
|
|
self.in_production_list = 0
|
2007-12-05 20:00:01 -06:00
|
|
|
self.first_document = 1
|
2007-12-05 15:11:36 -06:00
|
|
|
self.this_is_the_title = 1
|
2007-12-05 20:00:01 -06:00
|
|
|
self.literal_whitespace = 0
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
def astext(self):
|
|
|
|
return (HEADER % self.options) + \
|
2007-12-06 04:00:57 -06:00
|
|
|
highlighting.get_stylesheet('latex') + '\n\n' + \
|
2007-12-05 15:11:36 -06:00
|
|
|
u''.join(self.body) + \
|
|
|
|
(FOOTER % self.options)
|
|
|
|
|
|
|
|
def visit_document(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
if self.first_document == 1:
|
|
|
|
self.body.append('\\begin{document}\n\\maketitle\n\\tableofcontents\n')
|
|
|
|
self.first_document = 0
|
|
|
|
elif self.first_document == 0:
|
|
|
|
self.body.append('\n\\appendix\n')
|
|
|
|
self.first_document = -1
|
2007-12-07 14:27:52 -06:00
|
|
|
self.sectionlevel = self.top_sectionlevel
|
2007-12-05 15:11:36 -06:00
|
|
|
def depart_document(self, node):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def visit_highlightlang(self, node):
|
|
|
|
self.highlightlang = node['lang']
|
|
|
|
raise nodes.SkipNode
|
|
|
|
|
|
|
|
def visit_comment(self, node):
|
|
|
|
raise nodes.SkipNode
|
|
|
|
|
|
|
|
def visit_section(self, node):
|
|
|
|
if not self.this_is_the_title:
|
|
|
|
self.sectionlevel += 1
|
2007-12-05 20:00:01 -06:00
|
|
|
self.body.append('\n\n')
|
|
|
|
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)
|
2007-12-05 15:11:36 -06:00
|
|
|
def depart_section(self, node):
|
|
|
|
self.sectionlevel -= 1
|
|
|
|
|
2007-12-07 14:27:52 -06:00
|
|
|
def visit_topic(self, node):
|
|
|
|
raise nodes.SkipNode # XXX
|
|
|
|
|
|
|
|
def visit_sidebar(self, node):
|
|
|
|
raise nodes.SkipNode # XXX
|
|
|
|
|
2007-12-05 15:11:36 -06:00
|
|
|
def visit_glossary(self, node):
|
2007-12-16 06:20:41 -06:00
|
|
|
pass
|
|
|
|
def depart_glossary(self, node):
|
|
|
|
pass
|
2007-12-05 15:11:36 -06:00
|
|
|
|
2007-12-05 20:00:01 -06:00
|
|
|
def visit_productionlist(self, node):
|
2007-12-16 06:20:41 -06:00
|
|
|
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')
|
2007-12-05 15:11:36 -06:00
|
|
|
|
2007-12-05 20:00:01 -06:00
|
|
|
def visit_transition(self, node):
|
|
|
|
self.body.append('\n\n\\bigskip\\hrule{}\\bigskip\n\n')
|
2007-12-05 15:11:36 -06:00
|
|
|
def depart_transition(self, node):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def visit_title(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
if isinstance(node.parent, addnodes.seealso):
|
|
|
|
# the environment already handles this
|
|
|
|
raise nodes.SkipNode
|
|
|
|
elif self.this_is_the_title:
|
2007-12-05 15:11:36 -06:00
|
|
|
if len(node.children) != 1 and not isinstance(node.children[0], Text):
|
|
|
|
raise RuntimeError("title is not a Text node")
|
|
|
|
self.options['title'] = node.children[0].astext()
|
|
|
|
self.this_is_the_title = 0
|
|
|
|
raise nodes.SkipNode
|
|
|
|
elif isinstance(node.parent, nodes.section):
|
|
|
|
self.body.append(r'\%s{' % self.sectionnames[self.sectionlevel])
|
|
|
|
self.context.append('}\n')
|
|
|
|
else:
|
|
|
|
raise RuntimeError("XXX title without section")
|
2007-12-05 20:00:01 -06:00
|
|
|
self.in_title = 1
|
2007-12-05 15:11:36 -06:00
|
|
|
def depart_title(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
self.in_title = 0
|
2007-12-05 15:11:36 -06:00
|
|
|
self.body.append(self.context.pop())
|
|
|
|
|
|
|
|
def visit_field_list(self, node):
|
|
|
|
raise nodes.SkipNode # XXX
|
|
|
|
|
2007-12-05 20:00:01 -06:00
|
|
|
desc_map = {
|
|
|
|
'function' : 'funcdesc',
|
|
|
|
'class': 'classdesc',
|
|
|
|
#'classdesc*': ('class', '0'), XXX
|
|
|
|
'method': 'methoddesc',
|
|
|
|
'exception': 'excdesc',
|
|
|
|
#'excclassdesc': ('exception', '0(1)'), XXX
|
|
|
|
'data': 'datadesc',
|
|
|
|
'attribute': 'memberdesc',
|
|
|
|
'opcode': 'opcodedesc',
|
|
|
|
|
|
|
|
'cfunction': 'cfuncdesc',
|
|
|
|
'cmember': 'cmemberdesc',
|
|
|
|
'cmacro': 'csimplemacrodesc',
|
|
|
|
'ctype': 'ctypedesc',
|
|
|
|
'cvar': 'cvardesc',
|
|
|
|
|
|
|
|
'describe': 'describe',
|
|
|
|
'cmdoption': 'describe', # XXX?
|
|
|
|
'envvar': 'describe',
|
|
|
|
}
|
|
|
|
|
2007-12-05 15:11:36 -06:00
|
|
|
def visit_desc(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
self.descstack.append(Desc(node))
|
|
|
|
def depart_desc(self, node):
|
|
|
|
d = self.descstack.pop()
|
|
|
|
self.body.append("\\end{%s%s}\n" % (d.env, d.ni and 'ni' or ''))
|
|
|
|
|
|
|
|
def visit_desc_signature(self, node):
|
|
|
|
pass
|
|
|
|
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%s}" % (hyper, d.env, (d.ni and 'ni' or ''))
|
|
|
|
else:
|
|
|
|
t1 = "\n%s\\%sline%s" % (hyper, d.env[:-4], (d.ni and 'ni' or ''))
|
|
|
|
d.count += 1
|
|
|
|
if d.env in ('funcdesc', 'classdesc', 'excclassdesc'):
|
|
|
|
t2 = "{%s}{%s}" % (d.name, d.params)
|
|
|
|
elif d.env in ('datadesc', 'classdesc*', 'excdesc', 'csimplemacrodesc'):
|
|
|
|
t2 = "{%s}" % (d.name)
|
|
|
|
elif d.env == 'methoddesc':
|
|
|
|
t2 = "[%s]{%s}{%s}" % (d.cls, d.name, d.params)
|
|
|
|
elif d.env == 'memberdesc':
|
|
|
|
t2 = "[%s]{%s}" % (d.cls, d.name)
|
|
|
|
elif d.env == 'cfuncdesc':
|
|
|
|
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:
|
|
|
|
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):
|
|
|
|
self.descstack[-1].type = self.encode(node.astext().strip())
|
|
|
|
raise nodes.SkipNode
|
|
|
|
|
|
|
|
def visit_desc_name(self, node):
|
|
|
|
self.descstack[-1].name = self.encode(node.astext().strip())
|
|
|
|
raise nodes.SkipNode
|
|
|
|
|
|
|
|
def visit_desc_classname(self, node):
|
|
|
|
self.descstack[-1].cls = self.encode(node.astext().strip())
|
|
|
|
raise nodes.SkipNode
|
|
|
|
|
|
|
|
def visit_desc_parameterlist(self, node):
|
|
|
|
self.descstack[-1].params = 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):
|
|
|
|
pass
|
|
|
|
def depart_desc_content(self, node):
|
|
|
|
pass
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
def visit_seealso(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
self.body.append("\n\n\\begin{seealso}\n")
|
|
|
|
def depart_seealso(self, node):
|
|
|
|
self.body.append("\n\\end{seealso}\n")
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
def visit_rubric(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
if len(node.children) == 1 and node.children[0].astext() == 'Footnotes':
|
|
|
|
raise nodes.SkipNode
|
|
|
|
raise RuntimeError("rubric not supported except for footnotes heading")
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
def visit_footnote(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
# XXX not optimal, footnotes are at section end
|
|
|
|
num = node.children[0].astext().strip()
|
|
|
|
self.body.append('\\footnotetext[%s]{' % num)
|
|
|
|
def depart_footnote(self, node):
|
|
|
|
self.body.append('}')
|
|
|
|
|
|
|
|
def visit_label(self, node):
|
|
|
|
raise nodes.SkipNode
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
def visit_table(self, node):
|
|
|
|
self.tableSpec = TableSpec()
|
|
|
|
def depart_table(self, node):
|
|
|
|
self.tableSpec = None
|
|
|
|
|
|
|
|
def visit_colspec(self, node):
|
|
|
|
pass
|
|
|
|
def depart_colspec(self, node):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def visit_tgroup(self, node):
|
|
|
|
columnCount = int(node.get('cols', 0))
|
2007-12-05 20:00:01 -06:00
|
|
|
self.tableSpec.columnCount = columnCount
|
2007-12-05 15:11:36 -06:00
|
|
|
if columnCount == 2:
|
|
|
|
self.body.append('\\begin{tableii}{l|l}{textrm}')
|
|
|
|
elif columnCount == 3:
|
|
|
|
self.body.append('\\begin{tableiii}{l|l|l}{textrm}')
|
|
|
|
elif columnCount == 4:
|
|
|
|
self.body.append('\\begin{tableiv}{l|l|l|l}{textrm}')
|
|
|
|
elif columnCount == 5:
|
|
|
|
self.body.append('\\begin{tablev}{l|l|l|l|l}{textrm}')
|
2007-12-05 20:00:01 -06:00
|
|
|
else:
|
|
|
|
raise RuntimeError("XXX table with too many columns found")
|
2007-12-05 15:11:36 -06:00
|
|
|
def depart_tgroup(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
if self.tableSpec.columnCount == 2:
|
|
|
|
self.body.append('\n\\end{tableii}\n\n')
|
|
|
|
elif self.tableSpec.columnCount == 3:
|
|
|
|
self.body.append('\n\\end{tableiii}\n\n')
|
|
|
|
elif self.tableSpec.columnCount == 4:
|
|
|
|
self.body.append('\n\\end{tableiv}\n\n')
|
|
|
|
elif self.tableSpec.columnCount == 5:
|
|
|
|
self.body.append('\n\\end{tablev}\n\n')
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
def visit_thead(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
pass
|
2007-12-05 15:11:36 -06:00
|
|
|
def depart_thead(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
pass
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
def visit_tbody(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
pass
|
2007-12-05 15:11:36 -06:00
|
|
|
def depart_tbody(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
pass
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
def visit_row(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
if not self.tableSpec.firstRow:
|
|
|
|
if self.tableSpec.columnCount == 2:
|
2007-12-05 15:11:36 -06:00
|
|
|
self.body.append('\n\\lineii')
|
2007-12-05 20:00:01 -06:00
|
|
|
elif self.tableSpec.columnCount == 3:
|
2007-12-05 15:11:36 -06:00
|
|
|
self.body.append('\n\\lineiii')
|
2007-12-05 20:00:01 -06:00
|
|
|
elif self.tableSpec.columnCount == 4:
|
2007-12-05 15:11:36 -06:00
|
|
|
self.body.append('\n\\lineiv')
|
2007-12-05 20:00:01 -06:00
|
|
|
elif self.tableSpec.columnCount == 5:
|
2007-12-05 15:11:36 -06:00
|
|
|
self.body.append('\n\\linev')
|
|
|
|
def depart_row(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
if self.tableSpec.firstRow:
|
|
|
|
self.tableSpec.firstRow = 0
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
def visit_entry(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
if self.tableSpec.firstRow:
|
|
|
|
self.body.append('{%s}' % self.encode(node.astext().strip(' ')))
|
2007-12-05 15:11:36 -06:00
|
|
|
raise nodes.SkipNode
|
2007-12-05 20:00:01 -06:00
|
|
|
else:
|
|
|
|
self.body.append('{')
|
2007-12-05 15:11:36 -06:00
|
|
|
def depart_entry(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
if self.tableSpec.firstRow:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
self.body.append('}')
|
2007-12-05 15:11:36 -06:00
|
|
|
|
2007-12-07 14:27:52 -06:00
|
|
|
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
|
|
|
|
|
2007-12-05 15:11:36 -06:00
|
|
|
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):
|
2007-12-16 06:20:41 -06:00
|
|
|
if node.has_key('ids') and node['ids']:
|
|
|
|
self.body.append('\\hypertarget{%s}{}' % node['ids'][0])
|
2007-12-05 15:11:36 -06:00
|
|
|
self.body.append('\\item[')
|
|
|
|
def depart_term(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
self.body.append(']\n')
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
def visit_classifier(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
self.body.append('{[}')
|
2007-12-05 15:11:36 -06:00
|
|
|
def depart_classifier(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
self.body.append('{]}')
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
def visit_definition(self, node):
|
|
|
|
pass
|
|
|
|
def depart_definition(self, node):
|
|
|
|
self.body.append('\n')
|
|
|
|
|
|
|
|
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_note(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
self.body.append('\n\\begin{notice}[note]')
|
2007-12-05 15:11:36 -06:00
|
|
|
def depart_note(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
self.body.append('\\end{notice}\n')
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
def visit_warning(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
self.body.append('\n\\begin{notice}[warning]')
|
2007-12-05 15:11:36 -06:00
|
|
|
def depart_warning(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
self.body.append('\\end{notice}\n')
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
def visit_versionmodified(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
self.body.append('\\%s' % node['type'])
|
|
|
|
if node['type'] == 'deprecated':
|
|
|
|
self.body.append('{%s}{' % node['version'])
|
|
|
|
self.context.append('}')
|
|
|
|
else:
|
|
|
|
if len(node):
|
|
|
|
self.body.append('[')
|
|
|
|
self.context.append(']{%s}' % node['version'])
|
|
|
|
else:
|
|
|
|
self.body.append('{%s}' % node['version'])
|
|
|
|
self.context.append('')
|
2007-12-05 15:11:36 -06:00
|
|
|
def depart_versionmodified(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
self.body.append(self.context.pop())
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
def visit_target(self, node):
|
2007-12-16 06:20:41 -06:00
|
|
|
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)
|
|
|
|
return '}'
|
|
|
|
return ''
|
|
|
|
|
|
|
|
# XXX specialcase 'module-' targets: add \declaremodule
|
|
|
|
# XXX where to put \makemodindex
|
|
|
|
|
2007-12-05 15:11:36 -06:00
|
|
|
if not (node.has_key('refuri') or node.has_key('refid')
|
|
|
|
or node.has_key('refname')):
|
2007-12-05 20:00:01 -06:00
|
|
|
ctx = ''
|
2007-12-05 15:11:36 -06:00
|
|
|
for id in node['ids']:
|
2007-12-05 20:00:01 -06:00
|
|
|
if id not in self.written_ids:
|
|
|
|
self.written_ids.add(id)
|
2007-12-16 06:20:41 -06:00
|
|
|
ctx += add_target(id)
|
2007-12-05 20:00:01 -06:00
|
|
|
self.context.append(ctx)
|
|
|
|
elif node.has_key('refid') and node['refid'] not in self.written_ids:
|
2007-12-16 06:20:41 -06:00
|
|
|
self.context.append(add_target(node['refid']))
|
2007-12-05 20:00:01 -06:00
|
|
|
self.written_ids.add(node['refid'])
|
2007-12-05 15:11:36 -06:00
|
|
|
else:
|
|
|
|
self.context.append('')
|
|
|
|
def depart_target(self, node):
|
|
|
|
self.body.append(self.context.pop())
|
|
|
|
|
2007-12-16 06:20:41 -06:00
|
|
|
indextype_map = {
|
|
|
|
'module': 'refmodindex', # XXX: key?
|
|
|
|
'keyword': 'kwindex',
|
|
|
|
'operator': 'opindex',
|
|
|
|
'object': 'obindex',
|
|
|
|
'exception': 'exindex',
|
|
|
|
'statement': 'stindex',
|
|
|
|
'builtin': 'bifuncindex',
|
|
|
|
}
|
|
|
|
|
|
|
|
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))
|
|
|
|
self.body.append(r'\indexii{%s}{%s}' % parts)
|
|
|
|
elif type == 'triple':
|
|
|
|
parts = tuple(self.encode(x.strip()) for x in string.split(';', 2))
|
|
|
|
self.body.append(r'\indexiii{%s}{%s}{%s}' % parts)
|
|
|
|
elif type in self.indextype_map:
|
|
|
|
self.body.append(r'\%s{%s}' % (self.indextype_map[type],
|
|
|
|
self.encode(string)))
|
|
|
|
else:
|
|
|
|
raise RuntimeError('XXX unknown index entry type')
|
|
|
|
raise nodes.SkipNode
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
def visit_reference(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
uri = node.get('refuri', '')
|
|
|
|
if self.in_title or not uri:
|
|
|
|
self.context.append('')
|
|
|
|
elif uri.startswith(('mailto:', 'http:', '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('}')
|
2007-12-16 06:20:41 -06:00
|
|
|
elif uri.startswith('@token'):
|
|
|
|
if self.in_production_list:
|
|
|
|
self.body.append('\\token{')
|
|
|
|
else:
|
|
|
|
self.body.append('\\grammartoken{')
|
|
|
|
self.context.append('}')
|
2007-12-05 20:00:01 -06:00
|
|
|
else:
|
|
|
|
raise RuntimeError('XXX malformed reference target %s' % uri)
|
2007-12-05 15:11:36 -06:00
|
|
|
def depart_reference(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
self.body.append(self.context.pop())
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
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{') # XXX
|
|
|
|
def depart_literal_emphasis(self, node):
|
|
|
|
self.body.append('}')
|
|
|
|
|
|
|
|
def visit_strong(self, node):
|
|
|
|
self.body.append(r'\textbf{')
|
|
|
|
def depart_strong(self, node):
|
|
|
|
self.body.append('}')
|
|
|
|
|
2007-12-05 20:00:01 -06:00
|
|
|
def visit_title_reference(self, node):
|
2007-12-07 14:27:52 -06:00
|
|
|
self.body.append(r'\emph{')
|
|
|
|
def depart_title_reference(self, node):
|
|
|
|
self.body.append('}')
|
2007-12-05 20:00:01 -06:00
|
|
|
|
2007-12-05 15:11:36 -06:00
|
|
|
def visit_literal(self, node):
|
|
|
|
content = self.encode(node.astext().strip())
|
2007-12-05 20:00:01 -06:00
|
|
|
if self.in_title:
|
|
|
|
self.body.append(r'\texttt{%s}' % content)
|
|
|
|
elif re.search('[ \t\n]', content):
|
2007-12-05 15:11:36 -06:00
|
|
|
self.body.append(r'\samp{%s}' % content)
|
|
|
|
else:
|
|
|
|
self.body.append(r'\code{%s}' % content)
|
|
|
|
raise nodes.SkipNode
|
|
|
|
|
|
|
|
def visit_footnote_reference(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
self.body.append('\\footnotemark[%s]' % node.astext())
|
|
|
|
raise nodes.SkipNode
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
def visit_literal_block(self, node):
|
2007-12-06 04:00:57 -06:00
|
|
|
self.verbatim = ''
|
2007-12-05 15:11:36 -06:00
|
|
|
def depart_literal_block(self, node):
|
2007-12-07 14:27:52 -06:00
|
|
|
hlcode = highlighting.highlight_block(self.verbatim.rstrip('\n'),
|
|
|
|
self.highlightlang, 'latex')
|
|
|
|
# workaround for Pygments bug
|
|
|
|
hlcode = hlcode.replace('\n\\end{Verbatim}', '\\end{Verbatim}')
|
|
|
|
self.body.append('\n' + hlcode)
|
2007-12-06 04:00:57 -06:00
|
|
|
self.verbatim = None
|
2007-12-05 20:00:01 -06:00
|
|
|
visit_doctest_block = visit_literal_block
|
|
|
|
depart_doctest_block = depart_literal_block
|
2007-12-05 15:11:36 -06:00
|
|
|
|
|
|
|
def visit_line_block(self, node):
|
|
|
|
"""line-block:
|
|
|
|
* whitespace (including linebreaks) is significant
|
|
|
|
* inline markup is supported.
|
|
|
|
* serif typeface
|
|
|
|
"""
|
|
|
|
self.body.append('\\begin{flushleft}\n')
|
2007-12-05 20:00:01 -06:00
|
|
|
self.literal_whitespace = 1
|
2007-12-05 15:11:36 -06:00
|
|
|
def depart_line_block(self, node):
|
2007-12-05 20:00:01 -06:00
|
|
|
self.literal_whitespace = 0
|
2007-12-05 15:11:36 -06:00
|
|
|
self.body.append('\n\\end{flushleft}\n')
|
|
|
|
|
|
|
|
def visit_line(self, node):
|
|
|
|
pass
|
|
|
|
def depart_line(self, node):
|
|
|
|
pass
|
|
|
|
|
|
|
|
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')
|
|
|
|
|
|
|
|
replacements = [
|
2007-12-05 20:00:01 -06:00
|
|
|
(u"\\", u"\x00"),
|
2007-12-05 15:11:36 -06:00
|
|
|
(u"$", ur"\$"),
|
2007-12-05 20:00:01 -06:00
|
|
|
(r"%", ur"\%"),
|
2007-12-05 15:11:36 -06:00
|
|
|
(u"&", ur"\&"),
|
|
|
|
(u"#", ur"\#"),
|
|
|
|
(u"_", ur"\_"),
|
|
|
|
(u"{", ur"\{"),
|
|
|
|
(u"}", ur"\}"),
|
2007-12-05 20:00:01 -06:00
|
|
|
(u"[", ur"{[}"),
|
|
|
|
(u"]", ur"{]}"),
|
2007-12-05 15:11:36 -06:00
|
|
|
(u"¶", ur"\P{}"),
|
|
|
|
(u"§", ur"\S{}"),
|
|
|
|
(u"~", ur"\textasciitilde{}"),
|
|
|
|
(u"<", ur"\textless{}"),
|
|
|
|
(u">", ur"\textgreater{}"),
|
|
|
|
(u"^", ur"\textasciicircum{}"),
|
2007-12-05 20:00:01 -06:00
|
|
|
(u"\x00", ur"\textbackslash{}"),
|
2007-12-05 15:11:36 -06:00
|
|
|
]
|
|
|
|
|
|
|
|
def encode(self, text):
|
|
|
|
for x, y in self.replacements:
|
|
|
|
text = text.replace(x, y)
|
2007-12-05 20:00:01 -06:00
|
|
|
if self.literal_whitespace:
|
|
|
|
# Insert a blank before the newline, to avoid
|
|
|
|
# ! LaTeX Error: There's no line here to end.
|
|
|
|
text = text.replace("\n", '~\\\\\n').replace(" ", "~")
|
2007-12-05 15:11:36 -06:00
|
|
|
return text
|
|
|
|
|
|
|
|
def visit_Text(self, node):
|
2007-12-06 04:00:57 -06:00
|
|
|
if self.verbatim is not None:
|
2007-12-07 14:27:52 -06:00
|
|
|
self.verbatim += node.astext()
|
2007-12-06 04:00:57 -06:00
|
|
|
else:
|
|
|
|
self.body.append(self.encode(node.astext()))
|
2007-12-05 15:11:36 -06:00
|
|
|
def depart_Text(self, node):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def unknown_visit(self, node):
|
|
|
|
raise NotImplementedError("Unknown node: " + node.__class__.__name__)
|