buildframework/helium/external/python/lib/common/docutils-0.5-py2.5.egg/docutils/writers/latex2e/__init__.py
changeset 179 d8ac696cc51f
equal deleted inserted replaced
1:be27ed110b50 179:d8ac696cc51f
       
     1 # $Id: __init__.py 5333 2007-07-10 17:31:28Z grubert $
       
     2 # Author: Engelbert Gruber <grubert@users.sourceforge.net>
       
     3 # Copyright: This module has been placed in the public domain.
       
     4 
       
     5 """
       
     6 LaTeX2e document tree Writer.
       
     7 """
       
     8 
       
     9 __docformat__ = 'reStructuredText'
       
    10 
       
    11 # code contributions from several people included, thanks to all.
       
    12 # some named: David Abrahams, Julien Letessier, Lele Gaifax, and others.
       
    13 #
       
    14 # convention deactivate code by two # e.g. ##.
       
    15 
       
    16 import sys
       
    17 import time
       
    18 import re
       
    19 import string
       
    20 from types import ListType
       
    21 from docutils import frontend, nodes, languages, writers, utils
       
    22 from docutils.writers.newlatex2e import unicode_map
       
    23 
       
    24 from docutils.transforms.references import DanglingReferencesVisitor
       
    25 
       
    26 class Writer(writers.Writer):
       
    27 
       
    28     supported = ('latex','latex2e')
       
    29     """Formats this writer supports."""
       
    30 
       
    31     settings_spec = (
       
    32         'LaTeX-Specific Options',
       
    33         'The LaTeX "--output-encoding" default is "latin-1:strict".',
       
    34         (('Specify documentclass.  Default is "article".',
       
    35           ['--documentclass'],
       
    36           {'default': 'article', }),
       
    37          ('Specify document options.  Multiple options can be given, '
       
    38           'separated by commas.  Default is "10pt,a4paper".',
       
    39           ['--documentoptions'],
       
    40           {'default': '10pt,a4paper', }),
       
    41          ('Use LaTeX footnotes. LaTeX supports only numbered footnotes (does it?). '
       
    42           'Default: no, uses figures.',
       
    43           ['--use-latex-footnotes'],
       
    44           {'default': 0, 'action': 'store_true',
       
    45            'validator': frontend.validate_boolean}),
       
    46          ('Format for footnote references: one of "superscript" or '
       
    47           '"brackets".  Default is "superscript".',
       
    48           ['--footnote-references'],
       
    49           {'choices': ['superscript', 'brackets'], 'default': 'superscript',
       
    50            'metavar': '<format>',
       
    51            'overrides': 'trim_footnote_reference_space'}),
       
    52          ('Use LaTeX citations. '
       
    53           'Default: no, uses figures which might get mixed with images.',
       
    54           ['--use-latex-citations'],
       
    55           {'default': 0, 'action': 'store_true',
       
    56            'validator': frontend.validate_boolean}),
       
    57          ('Format for block quote attributions: one of "dash" (em-dash '
       
    58           'prefix), "parentheses"/"parens", or "none".  Default is "dash".',
       
    59           ['--attribution'],
       
    60           {'choices': ['dash', 'parentheses', 'parens', 'none'],
       
    61            'default': 'dash', 'metavar': '<format>'}),
       
    62          ('Specify a stylesheet file. The file will be "input" by latex in '
       
    63           'the document header.  Default is no stylesheet ("").  '
       
    64           'Overrides --stylesheet-path.',
       
    65           ['--stylesheet'],
       
    66           {'default': '', 'metavar': '<file>',
       
    67            'overrides': 'stylesheet_path'}),
       
    68          ('Specify a stylesheet file, relative to the current working '
       
    69           'directory.  Overrides --stylesheet.',
       
    70           ['--stylesheet-path'],
       
    71           {'metavar': '<file>', 'overrides': 'stylesheet'}),
       
    72          ('Table of contents by docutils (default) or LaTeX. LaTeX (writer) '
       
    73           'supports only one ToC per document, but docutils does not know of '
       
    74           'pagenumbers. LaTeX table of contents also means LaTeX generates '
       
    75           'sectionnumbers.',
       
    76           ['--use-latex-toc'],
       
    77           {'default': 0, 'action': 'store_true',
       
    78            'validator': frontend.validate_boolean}),
       
    79          ('Add parts on top of the section hierarchy.',
       
    80           ['--use-part-section'],
       
    81           {'default': 0, 'action': 'store_true',
       
    82            'validator': frontend.validate_boolean}),
       
    83          ('Let LaTeX print author and date, do not show it in docutils '
       
    84           'document info.',
       
    85           ['--use-latex-docinfo'],
       
    86           {'default': 0, 'action': 'store_true',
       
    87            'validator': frontend.validate_boolean}),
       
    88          ('Use LaTeX abstract environment for the documents abstract.'
       
    89           'Per default the abstract is an unnumbered section.',
       
    90           ['--use-latex-abstract'],
       
    91           {'default': 0, 'action': 'store_true',
       
    92            'validator': frontend.validate_boolean}),
       
    93          ('Color of any hyperlinks embedded in text '
       
    94           '(default: "blue", "0" to disable).',
       
    95           ['--hyperlink-color'], {'default': 'blue'}),
       
    96          ('Enable compound enumerators for nested enumerated lists '
       
    97           '(e.g. "1.2.a.ii").  Default: disabled.',
       
    98           ['--compound-enumerators'],
       
    99           {'default': None, 'action': 'store_true',
       
   100            'validator': frontend.validate_boolean}),
       
   101          ('Disable compound enumerators for nested enumerated lists.  This is '
       
   102           'the default.',
       
   103           ['--no-compound-enumerators'],
       
   104           {'action': 'store_false', 'dest': 'compound_enumerators'}),
       
   105          ('Enable section ("." subsection ...) prefixes for compound '
       
   106           'enumerators.  This has no effect without --compound-enumerators.  '
       
   107           'Default: disabled.',
       
   108           ['--section-prefix-for-enumerators'],
       
   109           {'default': None, 'action': 'store_true',
       
   110            'validator': frontend.validate_boolean}),
       
   111          ('Disable section prefixes for compound enumerators.  '
       
   112           'This is the default.',
       
   113           ['--no-section-prefix-for-enumerators'],
       
   114           {'action': 'store_false', 'dest': 'section_prefix_for_enumerators'}),
       
   115          ('Set the separator between section number and enumerator '
       
   116           'for compound enumerated lists.  Default is "-".',
       
   117           ['--section-enumerator-separator'],
       
   118           {'default': '-', 'metavar': '<char>'}),
       
   119          ('When possibile, use verbatim for literal-blocks. '
       
   120           'Default is to always use the mbox environment.',
       
   121           ['--use-verbatim-when-possible'],
       
   122           {'default': 0, 'action': 'store_true',
       
   123            'validator': frontend.validate_boolean}),
       
   124          ('Table style. "standard" with horizontal and vertical lines, '
       
   125           '"booktabs" (LaTeX booktabs style) only horizontal lines '
       
   126           'above and below the table and below the header or "nolines".  '
       
   127           'Default: "standard"',
       
   128           ['--table-style'],
       
   129           {'choices': ['standard', 'booktabs','nolines'], 'default': 'standard',
       
   130            'metavar': '<format>'}),
       
   131          ('LaTeX graphicx package option. '
       
   132           'Possible values are "dvips", "pdftex". "auto" includes LaTeX code '
       
   133           'to use "pdftex" if processing with pdf(la)tex and dvips otherwise. '
       
   134           'Default is no option.',
       
   135           ['--graphicx-option'],
       
   136           {'default': ''}),
       
   137          ('LaTeX font encoding. '
       
   138           'Possible values are "T1", "OT1", "" or some other fontenc option. '
       
   139           'The font encoding influences available symbols, e.g. "<<" as one '
       
   140           'character. Default is "" which leads to package "ae" (a T1 '
       
   141           'emulation using CM fonts).',
       
   142           ['--font-encoding'],
       
   143           {'default': ''}),
       
   144          ('Per default the latex-writer puts the reference title into '
       
   145           'hyperreferences. Specify "ref*" or "pageref*" to get the section '
       
   146           'number or the page number.',
       
   147           ['--reference-label'],
       
   148           {'default': None, }),
       
   149          ('Specify style and database for bibtex, for example '
       
   150           '"--use-bibtex=mystyle,mydb1,mydb2".',
       
   151           ['--use-bibtex'],
       
   152           {'default': None, }),
       
   153           ),)
       
   154 
       
   155     settings_defaults = {'output_encoding': 'latin-1'}
       
   156 
       
   157     relative_path_settings = ('stylesheet_path',)
       
   158 
       
   159     config_section = 'latex2e writer'
       
   160     config_section_dependencies = ('writers',)
       
   161 
       
   162     visitor_attributes = ("head_prefix", "head", 
       
   163             "body_prefix", "body", "body_suffix")
       
   164 
       
   165     output = None
       
   166     """Final translated form of `document`."""
       
   167 
       
   168     def __init__(self):
       
   169         writers.Writer.__init__(self)
       
   170         self.translator_class = LaTeXTranslator
       
   171 
       
   172     def translate(self):
       
   173         visitor = self.translator_class(self.document)
       
   174         self.document.walkabout(visitor)
       
   175         self.output = visitor.astext()
       
   176         # copy parts 
       
   177         for attr in self.visitor_attributes:
       
   178             setattr(self, attr, getattr(visitor, attr))
       
   179 
       
   180     def assemble_parts(self):
       
   181         writers.Writer.assemble_parts(self)
       
   182         for part in self.visitor_attributes:
       
   183             self.parts[part] = ''.join(getattr(self, part))
       
   184 
       
   185 
       
   186 """
       
   187 Notes on LaTeX
       
   188 --------------
       
   189 
       
   190 * LaTeX does not support multiple tocs in one document.
       
   191   (might be no limitation except for docutils documentation)
       
   192 
       
   193   The "minitoc" latex package can produce per-chapter tocs in
       
   194   book and report document classes.
       
   195 
       
   196 * width
       
   197 
       
   198   * linewidth - width of a line in the local environment
       
   199   * textwidth - the width of text on the page
       
   200 
       
   201   Maybe always use linewidth ?
       
   202 
       
   203   *Bug* inside a minipage a (e.g. Sidebar) the linewidth is
       
   204         not changed, needs fix in docutils so that tables
       
   205         are not too wide.
       
   206 
       
   207         So we add locallinewidth set it initially and
       
   208         on entering sidebar and reset on exit.
       
   209 """
       
   210 
       
   211 class Babel:
       
   212     """Language specifics for LaTeX."""
       
   213     # country code by a.schlock.
       
   214     # partly manually converted from iso and babel stuff, dialects and some
       
   215     _ISO639_TO_BABEL = {
       
   216         'no': 'norsk',     #XXX added by hand ( forget about nynorsk?)
       
   217         'gd': 'scottish',  #XXX added by hand
       
   218         'hu': 'magyar',    #XXX added by hand
       
   219         'pt': 'portuguese',#XXX added by hand
       
   220         'sl': 'slovenian',
       
   221         'af': 'afrikaans',
       
   222         'bg': 'bulgarian',
       
   223         'br': 'breton',
       
   224         'ca': 'catalan',
       
   225         'cs': 'czech',
       
   226         'cy': 'welsh',
       
   227         'da': 'danish',
       
   228         'fr': 'french',
       
   229         # french, francais, canadien, acadian
       
   230         'de': 'ngerman',  #XXX rather than german
       
   231         # ngerman, naustrian, german, germanb, austrian
       
   232         'el': 'greek',
       
   233         'en': 'english',
       
   234         # english, USenglish, american, UKenglish, british, canadian
       
   235         'eo': 'esperanto',
       
   236         'es': 'spanish',
       
   237         'et': 'estonian',
       
   238         'eu': 'basque',
       
   239         'fi': 'finnish',
       
   240         'ga': 'irish',
       
   241         'gl': 'galician',
       
   242         'he': 'hebrew',
       
   243         'hr': 'croatian',
       
   244         'hu': 'hungarian',
       
   245         'is': 'icelandic',
       
   246         'it': 'italian',
       
   247         'la': 'latin',
       
   248         'nl': 'dutch',
       
   249         'pl': 'polish',
       
   250         'pt': 'portuguese',
       
   251         'ro': 'romanian',
       
   252         'ru': 'russian',
       
   253         'sk': 'slovak',
       
   254         'sr': 'serbian',
       
   255         'sv': 'swedish',
       
   256         'tr': 'turkish',
       
   257         'uk': 'ukrainian'
       
   258     }
       
   259 
       
   260     def __init__(self,lang):
       
   261         self.language = lang
       
   262         # pdflatex does not produce double quotes for ngerman in tt.
       
   263         self.double_quote_replacment = None
       
   264         if re.search('^de',self.language):
       
   265             #self.quotes = ("\"`", "\"'")
       
   266             self.quotes = ('{\\glqq}', '{\\grqq}')
       
   267             self.double_quote_replacment = "{\\dq}"
       
   268         elif re.search('^it',self.language):
       
   269             self.quotes = ("``", "''")
       
   270             self.double_quote_replacment = r'{\char`\"}'
       
   271         else:
       
   272             self.quotes = ("``", "''")
       
   273         self.quote_index = 0
       
   274 
       
   275     def next_quote(self):
       
   276         q = self.quotes[self.quote_index]
       
   277         self.quote_index = (self.quote_index+1)%2
       
   278         return q
       
   279 
       
   280     def quote_quotes(self,text):
       
   281         t = None
       
   282         for part in text.split('"'):
       
   283             if t == None:
       
   284                 t = part
       
   285             else:
       
   286                 t += self.next_quote() + part
       
   287         return t
       
   288 
       
   289     def double_quotes_in_tt (self,text):
       
   290         if not self.double_quote_replacment:
       
   291             return text
       
   292         return text.replace('"', self.double_quote_replacment)
       
   293 
       
   294     def get_language(self):
       
   295         if self._ISO639_TO_BABEL.has_key(self.language):
       
   296             return self._ISO639_TO_BABEL[self.language]
       
   297         else:
       
   298             # support dialects.
       
   299             l = self.language.split("_")[0]
       
   300             if self._ISO639_TO_BABEL.has_key(l):
       
   301                 return self._ISO639_TO_BABEL[l]
       
   302         return None
       
   303 
       
   304 
       
   305 latex_headings = {
       
   306         'optionlist_environment' : [
       
   307               '\\newcommand{\\optionlistlabel}[1]{\\bf #1 \\hfill}\n'
       
   308               '\\newenvironment{optionlist}[1]\n'
       
   309               '{\\begin{list}{}\n'
       
   310               '  {\\setlength{\\labelwidth}{#1}\n'
       
   311               '   \\setlength{\\rightmargin}{1cm}\n'
       
   312               '   \\setlength{\\leftmargin}{\\rightmargin}\n'
       
   313               '   \\addtolength{\\leftmargin}{\\labelwidth}\n'
       
   314               '   \\addtolength{\\leftmargin}{\\labelsep}\n'
       
   315               '   \\renewcommand{\\makelabel}{\\optionlistlabel}}\n'
       
   316               '}{\\end{list}}\n',
       
   317               ],
       
   318         'lineblock_environment' : [
       
   319             '\\newlength{\\lineblockindentation}\n'
       
   320             '\\setlength{\\lineblockindentation}{2.5em}\n'
       
   321             '\\newenvironment{lineblock}[1]\n'
       
   322             '{\\begin{list}{}\n'
       
   323             '  {\\setlength{\\partopsep}{\\parskip}\n'
       
   324             '   \\addtolength{\\partopsep}{\\baselineskip}\n'
       
   325             '   \\topsep0pt\\itemsep0.15\\baselineskip\\parsep0pt\n'
       
   326             '   \\leftmargin#1}\n'
       
   327             ' \\raggedright}\n'
       
   328             '{\\end{list}}\n'
       
   329             ],
       
   330         'footnote_floats' : [
       
   331             '% begin: floats for footnotes tweaking.\n',
       
   332             '\\setlength{\\floatsep}{0.5em}\n',
       
   333             '\\setlength{\\textfloatsep}{\\fill}\n',
       
   334             '\\addtolength{\\textfloatsep}{3em}\n',
       
   335             '\\renewcommand{\\textfraction}{0.5}\n',
       
   336             '\\renewcommand{\\topfraction}{0.5}\n',
       
   337             '\\renewcommand{\\bottomfraction}{0.5}\n',
       
   338             '\\setcounter{totalnumber}{50}\n',
       
   339             '\\setcounter{topnumber}{50}\n',
       
   340             '\\setcounter{bottomnumber}{50}\n',
       
   341             '% end floats for footnotes\n',
       
   342             ],
       
   343         'some_commands' : [
       
   344             '% some commands, that could be overwritten in the style file.\n'
       
   345             '\\newcommand{\\rubric}[1]'
       
   346             '{\\subsection*{~\\hfill {\\it #1} \\hfill ~}}\n'
       
   347             '\\newcommand{\\titlereference}[1]{\\textsl{#1}}\n'
       
   348             '% end of "some commands"\n',
       
   349             ]
       
   350         }
       
   351 
       
   352 class DocumentClass:
       
   353     """Details of a LaTeX document class."""
       
   354 
       
   355     def __init__(self, document_class, with_part=False):
       
   356         self.document_class = document_class
       
   357         self._with_part = with_part
       
   358 
       
   359     def section(self, level):
       
   360         """ Return the section name at the given level for the specific
       
   361             document class.
       
   362 
       
   363             Level is 1,2,3..., as level 0 is the title."""
       
   364 
       
   365         sections = [ 'section', 'subsection', 'subsubsection', 
       
   366                      'paragraph', 'subparagraph' ]
       
   367         if self.document_class in ('book', 'report', 'scrreprt', 'scrbook'):
       
   368             sections.insert(0, 'chapter')
       
   369         if self._with_part:
       
   370             sections.insert(0, 'part')
       
   371         if level <= len(sections):
       
   372             return sections[level-1]
       
   373         else:
       
   374             return sections[-1]
       
   375 
       
   376 class Table:
       
   377     """ Manage a table while traversing.
       
   378         Maybe change to a mixin defining the visit/departs, but then
       
   379         class Table internal variables are in the Translator.
       
   380 
       
   381         Table style might be 
       
   382         
       
   383         * standard: horizontal and vertical lines
       
   384         * booktabs (requires booktabs latex package): only horizontal lines
       
   385         * nolines, borderless : no lines
       
   386     """
       
   387     def __init__(self,latex_type,table_style):
       
   388         self._latex_type = latex_type
       
   389         self._table_style = table_style
       
   390         self._open = 0
       
   391         # miscellaneous attributes
       
   392         self._attrs = {}
       
   393         self._col_width = []
       
   394         self._rowspan = []
       
   395         self.stubs = []
       
   396 
       
   397     def open(self):
       
   398         self._open = 1
       
   399         self._col_specs = []
       
   400         self.caption = None
       
   401         self._attrs = {}
       
   402         self._in_head = 0 # maybe context with search
       
   403     def close(self):
       
   404         self._open = 0
       
   405         self._col_specs = None
       
   406         self.caption = None
       
   407         self._attrs = {}
       
   408         self.stubs = []
       
   409     def is_open(self):
       
   410         return self._open
       
   411 
       
   412     def set_table_style(self, table_style):
       
   413         if not table_style in ('standard','booktabs','borderless','nolines'):
       
   414             return
       
   415         self._table_style = table_style
       
   416 
       
   417     def used_packages(self):
       
   418         if self._table_style == 'booktabs':
       
   419             return '\\usepackage{booktabs}\n'
       
   420         return ''
       
   421     def get_latex_type(self):
       
   422         return self._latex_type
       
   423 
       
   424     def set(self,attr,value):
       
   425         self._attrs[attr] = value
       
   426     def get(self,attr):
       
   427         if self._attrs.has_key(attr):
       
   428             return self._attrs[attr]
       
   429         return None
       
   430     def get_vertical_bar(self):
       
   431         if self._table_style == 'standard':
       
   432             return '|'
       
   433         return ''
       
   434     # horizontal lines are drawn below a row, because we.
       
   435     def get_opening(self):
       
   436         if self._latex_type == 'longtable':
       
   437             # otherwise longtable might move before paragraph and subparagraph
       
   438             prefix = '\\leavevmode\n'
       
   439         else:
       
   440             prefix = ''
       
   441         return '%s\\begin{%s}[c]' % (prefix, self._latex_type)
       
   442     def get_closing(self):
       
   443         line = ""
       
   444         if self._table_style == 'booktabs':
       
   445             line = '\\bottomrule\n'
       
   446         elif self._table_style == 'standard':
       
   447             lines = '\\hline\n'
       
   448         return '%s\\end{%s}' % (line,self._latex_type)
       
   449 
       
   450     def visit_colspec(self, node):
       
   451         self._col_specs.append(node)
       
   452         # "stubs" list is an attribute of the tgroup element:
       
   453         self.stubs.append(node.attributes.get('stub'))
       
   454 
       
   455     def get_colspecs(self):
       
   456         """
       
   457         Return column specification for longtable.
       
   458 
       
   459         Assumes reST line length being 80 characters.
       
   460         Table width is hairy.
       
   461 
       
   462         === ===
       
   463         ABC DEF
       
   464         === ===
       
   465 
       
   466         usually gets to narrow, therefore we add 1 (fiddlefactor).
       
   467         """
       
   468         width = 80
       
   469 
       
   470         total_width = 0.0
       
   471         # first see if we get too wide.
       
   472         for node in self._col_specs:
       
   473             colwidth = float(node['colwidth']+1) / width
       
   474             total_width += colwidth
       
   475         self._col_width = []
       
   476         self._rowspan = []
       
   477         # donot make it full linewidth
       
   478         factor = 0.93
       
   479         if total_width > 1.0:
       
   480             factor /= total_width
       
   481         bar = self.get_vertical_bar()
       
   482         latex_table_spec = ""
       
   483         for node in self._col_specs:
       
   484             colwidth = factor * float(node['colwidth']+1) / width
       
   485             self._col_width.append(colwidth+0.005)
       
   486             self._rowspan.append(0)
       
   487             latex_table_spec += "%sp{%.3f\\locallinewidth}" % (bar,colwidth+0.005)
       
   488         return latex_table_spec+bar
       
   489 
       
   490     def get_column_width(self):
       
   491         """ return columnwidth for current cell (not multicell)
       
   492         """
       
   493         return "%.2f\\locallinewidth" % self._col_width[self._cell_in_row-1]
       
   494 
       
   495     def visit_thead(self):
       
   496         self._in_thead = 1
       
   497         if self._table_style == 'standard':
       
   498             return ['\\hline\n']
       
   499         elif self._table_style == 'booktabs':
       
   500             return ['\\toprule\n']
       
   501         return []
       
   502     def depart_thead(self):
       
   503         a = []
       
   504         #if self._table_style == 'standard':
       
   505         #    a.append('\\hline\n')
       
   506         if self._table_style == 'booktabs':
       
   507             a.append('\\midrule\n')
       
   508         if self._latex_type == 'longtable':
       
   509             a.append('\\endhead\n')
       
   510         # for longtable one could add firsthead, foot and lastfoot
       
   511         self._in_thead = 0
       
   512         return a
       
   513     def visit_row(self):
       
   514         self._cell_in_row = 0
       
   515     def depart_row(self):
       
   516         res = [' \\\\\n']
       
   517         self._cell_in_row = None  # remove cell counter
       
   518         for i in range(len(self._rowspan)):
       
   519             if (self._rowspan[i]>0):
       
   520                 self._rowspan[i] -= 1
       
   521 
       
   522         if self._table_style == 'standard':
       
   523             rowspans = []
       
   524             for i in range(len(self._rowspan)):
       
   525                 if (self._rowspan[i]<=0):
       
   526                     rowspans.append(i+1)
       
   527             if len(rowspans)==len(self._rowspan):
       
   528                 res.append('\\hline\n')
       
   529             else:
       
   530                 cline = ''
       
   531                 rowspans.reverse()
       
   532                 # TODO merge clines
       
   533                 while 1:
       
   534                     try:
       
   535                         c_start = rowspans.pop()
       
   536                     except:
       
   537                         break
       
   538                     cline += '\\cline{%d-%d}\n' % (c_start,c_start)
       
   539                 res.append(cline)
       
   540         return res
       
   541 
       
   542     def set_rowspan(self,cell,value):
       
   543         try:
       
   544             self._rowspan[cell] = value
       
   545         except:
       
   546             pass
       
   547     def get_rowspan(self,cell):
       
   548         try:
       
   549             return self._rowspan[cell]
       
   550         except:
       
   551             return 0
       
   552     def get_entry_number(self):
       
   553         return self._cell_in_row
       
   554     def visit_entry(self):
       
   555         self._cell_in_row += 1
       
   556     def is_stub_column(self):
       
   557         if len(self.stubs) >= self._cell_in_row:
       
   558             return self.stubs[self._cell_in_row-1]
       
   559         return False
       
   560 
       
   561 
       
   562 class LaTeXTranslator(nodes.NodeVisitor):
       
   563 
       
   564     # When options are given to the documentclass, latex will pass them
       
   565     # to other packages, as done with babel.
       
   566     # Dummy settings might be taken from document settings
       
   567     
       
   568     # Templates
       
   569     # ---------
       
   570     
       
   571     latex_head = '\\documentclass[%s]{%s}\n'
       
   572     linking = '\\usepackage[colorlinks=%s,linkcolor=%s,urlcolor=%s]{hyperref}\n'
       
   573     stylesheet = '\\input{%s}\n'
       
   574     # add a generated on day , machine by user using docutils version.
       
   575     generator = '% generated by Docutils <http://docutils.sourceforge.net/>\n'
       
   576     # Config setting defaults
       
   577     # -----------------------
       
   578 
       
   579     # use latex tableofcontents or let docutils do it.
       
   580     use_latex_toc = 0
       
   581 
       
   582     # TODO: use mixins for different implementations.
       
   583     # list environment for docinfo. else tabularx
       
   584     use_optionlist_for_docinfo = 0 # NOT YET IN USE
       
   585 
       
   586     # Use compound enumerations (1.A.1.)
       
   587     compound_enumerators = 0
       
   588 
       
   589     # If using compound enumerations, include section information.
       
   590     section_prefix_for_enumerators = 0
       
   591 
       
   592     # This is the character that separates the section ("." subsection ...)
       
   593     # prefix from the regular list enumerator.
       
   594     section_enumerator_separator = '-'
       
   595 
       
   596     # default link color
       
   597     hyperlink_color = "blue"
       
   598 
       
   599     def __init__(self, document):
       
   600         nodes.NodeVisitor.__init__(self, document)
       
   601         self.settings = settings = document.settings
       
   602         self.latex_encoding = self.to_latex_encoding(settings.output_encoding)
       
   603         self.use_latex_toc = settings.use_latex_toc
       
   604         self.use_latex_docinfo = settings.use_latex_docinfo
       
   605         self.use_latex_footnotes = settings.use_latex_footnotes
       
   606         self._use_latex_citations = settings.use_latex_citations
       
   607         self._reference_label = settings.reference_label
       
   608         self.hyperlink_color = settings.hyperlink_color
       
   609         self.compound_enumerators = settings.compound_enumerators
       
   610         self.font_encoding = settings.font_encoding
       
   611         self.section_prefix_for_enumerators = (
       
   612             settings.section_prefix_for_enumerators)
       
   613         self.section_enumerator_separator = (
       
   614             settings.section_enumerator_separator.replace('_', '\\_'))
       
   615         if self.hyperlink_color == '0':
       
   616             self.hyperlink_color = 'black'
       
   617             self.colorlinks = 'false'
       
   618         else:
       
   619             self.colorlinks = 'true'
       
   620 
       
   621         if self.settings.use_bibtex:
       
   622             self.bibtex = self.settings.use_bibtex.split(",",1)
       
   623             # TODO avoid errors on not declared citations.
       
   624         else:
       
   625             self.bibtex = None
       
   626         # language: labels, bibliographic_fields, and author_separators.
       
   627         # to allow writing labes for specific languages.
       
   628         self.language = languages.get_language(settings.language_code)
       
   629         self.babel = Babel(settings.language_code)
       
   630         self.author_separator = self.language.author_separators[0]
       
   631         self.d_options = self.settings.documentoptions
       
   632         if self.babel.get_language():
       
   633             self.d_options += ',%s' % self.babel.get_language()
       
   634 
       
   635         self.d_class = DocumentClass(settings.documentclass, 
       
   636                                      settings.use_part_section)
       
   637         # object for a table while proccessing.
       
   638         self.table_stack = []
       
   639         self.active_table = Table('longtable',settings.table_style)
       
   640 
       
   641         # HACK.  Should have more sophisticated typearea handling.
       
   642         if settings.documentclass.find('scr') == -1:
       
   643             self.typearea = '\\usepackage[DIV12]{typearea}\n'
       
   644         else:
       
   645             if self.d_options.find('DIV') == -1 and self.d_options.find('BCOR') == -1:
       
   646                 self.typearea = '\\typearea{12}\n'
       
   647             else:
       
   648                 self.typearea = ''
       
   649 
       
   650         if self.font_encoding == 'OT1':
       
   651             fontenc_header = ''
       
   652         elif self.font_encoding == '':
       
   653             fontenc_header = '\\usepackage{ae}\n\\usepackage{aeguill}\n'
       
   654         else:
       
   655             fontenc_header = '\\usepackage[%s]{fontenc}\n' % (self.font_encoding,)
       
   656         if self.latex_encoding.startswith('utf8'):
       
   657             input_encoding = '\\usepackage{ucs}\n\\usepackage[utf8x]{inputenc}\n'
       
   658         else:
       
   659             input_encoding = '\\usepackage[%s]{inputenc}\n' % self.latex_encoding
       
   660         if self.settings.graphicx_option == '':
       
   661             self.graphicx_package = '\\usepackage{graphicx}\n'
       
   662         elif self.settings.graphicx_option.lower() == 'auto':
       
   663             self.graphicx_package = '\n'.join(
       
   664                 ('%Check if we are compiling under latex or pdflatex',
       
   665                  '\\ifx\\pdftexversion\\undefined',
       
   666                  '  \\usepackage{graphicx}',
       
   667                  '\\else',
       
   668                  '  \\usepackage[pdftex]{graphicx}',
       
   669                  '\\fi\n'))
       
   670         else:
       
   671             self.graphicx_package = (
       
   672                 '\\usepackage[%s]{graphicx}\n' % self.settings.graphicx_option)
       
   673 
       
   674         self.head_prefix = [
       
   675               self.latex_head % (self.d_options,self.settings.documentclass),
       
   676               '\\usepackage{babel}\n',     # language is in documents settings.
       
   677               fontenc_header,
       
   678               '\\usepackage{shortvrb}\n',  # allows verb in footnotes.
       
   679               input_encoding,
       
   680               # * tabularx: for docinfo, automatic width of columns, always on one page.
       
   681               '\\usepackage{tabularx}\n',
       
   682               '\\usepackage{longtable}\n',
       
   683               self.active_table.used_packages(),
       
   684               # possible other packages.
       
   685               # * fancyhdr
       
   686               # * ltxtable is a combination of tabularx and longtable (pagebreaks).
       
   687               #   but ??
       
   688               #
       
   689               # extra space between text in tables and the line above them
       
   690               '\\setlength{\\extrarowheight}{2pt}\n',
       
   691               '\\usepackage{amsmath}\n',   # what fore amsmath.
       
   692               self.graphicx_package,
       
   693               '\\usepackage{color}\n',
       
   694               '\\usepackage{multirow}\n',
       
   695               '\\usepackage{ifthen}\n',   # before hyperref!
       
   696               self.linking % (self.colorlinks, self.hyperlink_color, self.hyperlink_color),
       
   697               self.typearea,
       
   698               self.generator,
       
   699               # latex lengths
       
   700               '\\newlength{\\admonitionwidth}\n',
       
   701               '\\setlength{\\admonitionwidth}{0.9\\textwidth}\n'
       
   702               # width for docinfo tablewidth
       
   703               '\\newlength{\\docinfowidth}\n',
       
   704               '\\setlength{\\docinfowidth}{0.9\\textwidth}\n'
       
   705               # linewidth of current environment, so tables are not wider
       
   706               # than the sidebar: using locallinewidth seems to defer evaluation
       
   707               # of linewidth, this is fixing it.
       
   708               '\\newlength{\\locallinewidth}\n',
       
   709               # will be set later.
       
   710               ]
       
   711         self.head_prefix.extend( latex_headings['optionlist_environment'] )
       
   712         self.head_prefix.extend( latex_headings['lineblock_environment'] )
       
   713         self.head_prefix.extend( latex_headings['footnote_floats'] )
       
   714         self.head_prefix.extend( latex_headings['some_commands'] )
       
   715         ## stylesheet is last: so it might be possible to overwrite defaults.
       
   716         stylesheet = utils.get_stylesheet_reference(settings)
       
   717         if stylesheet:
       
   718             settings.record_dependencies.add(stylesheet)
       
   719             self.head_prefix.append(self.stylesheet % (stylesheet))
       
   720 
       
   721         if self.linking: # and maybe check for pdf
       
   722             self.pdfinfo = [ ]
       
   723             self.pdfauthor = None
       
   724             # pdftitle, pdfsubject, pdfauthor, pdfkeywords, 
       
   725             # pdfcreator, pdfproducer
       
   726         else:
       
   727             self.pdfinfo = None
       
   728         # NOTE: Latex wants a date and an author, rst puts this into
       
   729         #   docinfo, so normally we do not want latex author/date handling.
       
   730         # latex article has its own handling of date and author, deactivate.
       
   731         # self.astext() adds \title{...} \author{...} \date{...}, even if the
       
   732         # "..." are empty strings.
       
   733         self.head = [ ]
       
   734         # separate title, so we can appen subtitle.
       
   735         self.title = ''
       
   736         # if use_latex_docinfo: collects lists of author/organization/contact/address lines
       
   737         self.author_stack = []
       
   738         self.date = ''
       
   739 
       
   740         self.body_prefix = ['\\raggedbottom\n']
       
   741         self.body = []
       
   742         self.body_suffix = ['\n']
       
   743         self.section_level = 0
       
   744         self.context = []
       
   745         self.topic_classes = []
       
   746         # column specification for tables
       
   747         self.table_caption = None
       
   748         
       
   749         # Flags to encode
       
   750         # ---------------
       
   751         # verbatim: to tell encode not to encode.
       
   752         self.verbatim = 0
       
   753         # insert_newline: to tell encode to replace blanks by "~".
       
   754         self.insert_none_breaking_blanks = 0
       
   755         # insert_newline: to tell encode to add latex newline.
       
   756         self.insert_newline = 0
       
   757         # mbox_newline: to tell encode to add mbox and newline.
       
   758         self.mbox_newline = 0
       
   759         # inside citation reference labels underscores dont need to be escaped.
       
   760         self.inside_citation_reference_label = 0
       
   761 
       
   762         # Stack of section counters so that we don't have to use_latex_toc.
       
   763         # This will grow and shrink as processing occurs.
       
   764         # Initialized for potential first-level sections.
       
   765         self._section_number = [0]
       
   766 
       
   767         # The current stack of enumerations so that we can expand
       
   768         # them into a compound enumeration.  
       
   769         self._enumeration_counters = []
       
   770 
       
   771         # The maximum number of enumeration counters we've used.
       
   772         # If we go beyond this number, we need to create a new
       
   773         # counter; otherwise, just reuse an old one.
       
   774         self._max_enumeration_counters = 0
       
   775 
       
   776         self._bibitems = []
       
   777 
       
   778         # docinfo.
       
   779         self.docinfo = None
       
   780         # inside literal block: no quote mangling.
       
   781         self.literal_block = 0
       
   782         self.literal_block_stack = []
       
   783         self.literal = 0
       
   784         # true when encoding in math mode
       
   785         self.mathmode = 0
       
   786 
       
   787     def to_latex_encoding(self,docutils_encoding):
       
   788         """
       
   789         Translate docutils encoding name into latex's.
       
   790 
       
   791         Default fallback method is remove "-" and "_" chars from docutils_encoding.
       
   792 
       
   793         """
       
   794         tr = {  "iso-8859-1": "latin1",     # west european
       
   795                 "iso-8859-2": "latin2",     # east european
       
   796                 "iso-8859-3": "latin3",     # esperanto, maltese
       
   797                 "iso-8859-4": "latin4",     # north european,scandinavian, baltic
       
   798                 "iso-8859-5": "iso88595",   # cyrillic (ISO)
       
   799                 "iso-8859-9": "latin5",     # turkish
       
   800                 "iso-8859-15": "latin9",    # latin9, update to latin1.
       
   801                 "mac_cyrillic": "maccyr",   # cyrillic (on Mac)
       
   802                 "windows-1251": "cp1251",   # cyrillic (on Windows)
       
   803                 "koi8-r": "koi8-r",         # cyrillic (Russian)
       
   804                 "koi8-u": "koi8-u",         # cyrillic (Ukrainian)
       
   805                 "windows-1250": "cp1250",   #
       
   806                 "windows-1252": "cp1252",   #
       
   807                 "us-ascii": "ascii",        # ASCII (US)
       
   808                 # unmatched encodings
       
   809                 #"": "applemac",
       
   810                 #"": "ansinew",  # windows 3.1 ansi
       
   811                 #"": "ascii",    # ASCII encoding for the range 32--127.
       
   812                 #"": "cp437",    # dos latine us
       
   813                 #"": "cp850",    # dos latin 1
       
   814                 #"": "cp852",    # dos latin 2
       
   815                 #"": "decmulti",
       
   816                 #"": "latin10",
       
   817                 #"iso-8859-6": ""   # arabic
       
   818                 #"iso-8859-7": ""   # greek
       
   819                 #"iso-8859-8": ""   # hebrew
       
   820                 #"iso-8859-10": ""   # latin6, more complete iso-8859-4
       
   821              }
       
   822         if tr.has_key(docutils_encoding.lower()):
       
   823             return tr[docutils_encoding.lower()]
       
   824         # convert: latin-1 and utf-8 and similar things
       
   825         return docutils_encoding.replace("_", "").replace("-", "").lower()
       
   826 
       
   827     def language_label(self, docutil_label):
       
   828         return self.language.labels[docutil_label]
       
   829 
       
   830     latex_equivalents = {
       
   831         u'\u00A0' : '~',
       
   832         u'\u2013' : '{--}',
       
   833         u'\u2014' : '{---}',
       
   834         u'\u2018' : '`',
       
   835         u'\u2019' : '\'',
       
   836         u'\u201A' : ',',
       
   837         u'\u201C' : '``',
       
   838         u'\u201D' : '\'\'',
       
   839         u'\u201E' : ',,',
       
   840         u'\u2020' : '{\\dag}',
       
   841         u'\u2021' : '{\\ddag}',
       
   842         u'\u2026' : '{\\dots}',
       
   843         u'\u2122' : '{\\texttrademark}',
       
   844         u'\u21d4' : '{$\\Leftrightarrow$}',
       
   845         # greek alphabet ?
       
   846     }
       
   847 
       
   848     def unicode_to_latex(self,text):
       
   849         # see LaTeX codec
       
   850         # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252124
       
   851         # Only some special chracters are translated, for documents with many
       
   852         # utf-8 chars one should use the LaTeX unicode package.
       
   853         for uchar in self.latex_equivalents.keys():
       
   854             text = text.replace(uchar,self.latex_equivalents[uchar])
       
   855         return text
       
   856 
       
   857     def ensure_math(self, text):
       
   858         if not self.__dict__.has_key('ensure_math_re'):
       
   859             chars = {
       
   860                 # lnot,pm,twosuperior,threesuperior,mu,onesuperior,times,div
       
   861                 'latin1' : '\xac\xb1\xb2\xb3\xb5\xb9\xd7\xf7' ,
       
   862                 # also latin5 and latin9
       
   863                 }
       
   864             self.ensure_math_re = re.compile('([%s])' % chars['latin1'])
       
   865         text = self.ensure_math_re.sub(r'\\ensuremath{\1}', text)
       
   866         return text
       
   867 
       
   868     def encode(self, text):
       
   869         """
       
   870         Encode special characters (``# $ % & ~ _ ^ \ { }``) in `text` & return
       
   871         """
       
   872         # Escaping with a backslash does not help with backslashes, ~ and ^.
       
   873 
       
   874         #     < > are only available in math-mode or tt font. (really ?)
       
   875         #     $ starts math- mode.
       
   876         # AND quotes
       
   877         if self.verbatim:
       
   878             return text
       
   879         # compile the regexps once. do it here so one can see them.
       
   880         #
       
   881         # first the braces.
       
   882         if not self.__dict__.has_key('encode_re_braces'):
       
   883             self.encode_re_braces = re.compile(r'([{}])')
       
   884         text = self.encode_re_braces.sub(r'{\\\1}',text)
       
   885         if not self.__dict__.has_key('encode_re_bslash'):
       
   886             # find backslash: except in the form '{\{}' or '{\}}'.
       
   887             self.encode_re_bslash = re.compile(r'(?<!{)(\\)(?![{}]})')
       
   888         # then the backslash: except in the form from line above:
       
   889         # either '{\{}' or '{\}}'.
       
   890         text = self.encode_re_bslash.sub(r'{\\textbackslash}', text)
       
   891 
       
   892         # then dollar
       
   893         text = text.replace("$", '{\\$}')
       
   894         if not ( self.literal_block or self.literal or self.mathmode ):
       
   895             # the vertical bar: in mathmode |,\vert or \mid
       
   896             #   in textmode \textbar
       
   897             text = text.replace("|", '{\\textbar}')
       
   898             text = text.replace("<", '{\\textless}')
       
   899             text = text.replace(">", '{\\textgreater}')
       
   900         # then
       
   901         text = text.replace("&", '{\\&}')
       
   902         # the ^:
       
   903         # * verb|^| does not work in mbox.
       
   904         # * mathmode has wedge. hat{~} would also work.
       
   905         # text = text.replace("^", '{\\ensuremath{^\\wedge}}')
       
   906         text = text.replace("^", '{\\textasciicircum}')
       
   907         text = text.replace("%", '{\\%}')
       
   908         text = text.replace("#", '{\\#}')
       
   909         text = text.replace("~", '{\\textasciitilde}')
       
   910         # Separate compound characters, e.g. "--" to "-{}-".  (The
       
   911         # actual separation is done later; see below.)
       
   912         separate_chars = '-'
       
   913         if self.literal_block or self.literal:
       
   914             # In monospace-font, we also separate ",,", "``" and "''"
       
   915             # and some other characters which can't occur in
       
   916             # non-literal text.
       
   917             separate_chars += ',`\'"<>'
       
   918             # pdflatex does not produce doublequotes for ngerman.
       
   919             text = self.babel.double_quotes_in_tt(text)
       
   920             if self.font_encoding == 'OT1':
       
   921                 # We're using OT1 font-encoding and have to replace
       
   922                 # underscore by underlined blank, because this has
       
   923                 # correct width.
       
   924                 text = text.replace('_', '{\\underline{ }}')
       
   925                 # And the tt-backslash doesn't work in OT1, so we use
       
   926                 # a mirrored slash.
       
   927                 text = text.replace('\\textbackslash', '\\reflectbox{/}')
       
   928             else:
       
   929                 text = text.replace('_', '{\\_}')
       
   930         else:
       
   931             text = self.babel.quote_quotes(text)
       
   932             if not self.inside_citation_reference_label:
       
   933                 text = text.replace("_", '{\\_}')
       
   934         for char in separate_chars * 2:
       
   935             # Do it twice ("* 2") becaues otherwise we would replace
       
   936             # "---" by "-{}--".
       
   937             text = text.replace(char + char, char + '{}' + char)
       
   938         if self.insert_newline or self.literal_block:
       
   939             # Insert a blank before the newline, to avoid
       
   940             # ! LaTeX Error: There's no line here to end.
       
   941             text = text.replace("\n", '~\\\\\n')
       
   942         elif self.mbox_newline:
       
   943             if self.literal_block:
       
   944                 closings = "}" * len(self.literal_block_stack)
       
   945                 openings = "".join(self.literal_block_stack)
       
   946             else:
       
   947                 closings = ""
       
   948                 openings = ""
       
   949             text = text.replace("\n", "%s}\\\\\n\\mbox{%s" % (closings,openings))
       
   950         text = text.replace('[', '{[}').replace(']', '{]}')
       
   951         if self.insert_none_breaking_blanks:
       
   952             text = text.replace(' ', '~')
       
   953         if self.latex_encoding != 'utf8':
       
   954             text = self.unicode_to_latex(text)
       
   955             text = self.ensure_math(text)
       
   956         return text
       
   957 
       
   958     def attval(self, text,
       
   959                whitespace=re.compile('[\n\r\t\v\f]')):
       
   960         """Cleanse, encode, and return attribute value text."""
       
   961         return self.encode(whitespace.sub(' ', text))
       
   962 
       
   963     def astext(self):
       
   964         if self.pdfinfo is not None and self.pdfauthor:
       
   965             self.pdfinfo.append('pdfauthor={%s}' % self.pdfauthor)
       
   966         if self.pdfinfo:
       
   967             pdfinfo = '\\hypersetup{\n' + ',\n'.join(self.pdfinfo) + '\n}\n'
       
   968         else:
       
   969             pdfinfo = ''
       
   970         head = '\\title{%s}\n\\author{%s}\n\\date{%s}\n' % \
       
   971                (self.title,
       
   972                 ' \\and\n'.join(['~\\\\\n'.join(author_lines)
       
   973                                  for author_lines in self.author_stack]),
       
   974                 self.date)
       
   975         return ''.join(self.head_prefix + [head] + self.head + [pdfinfo]
       
   976                         + self.body_prefix  + self.body + self.body_suffix)
       
   977 
       
   978     def visit_Text(self, node):
       
   979         self.body.append(self.encode(node.astext()))
       
   980 
       
   981     def depart_Text(self, node):
       
   982         pass
       
   983 
       
   984     def visit_address(self, node):
       
   985         self.visit_docinfo_item(node, 'address')
       
   986 
       
   987     def depart_address(self, node):
       
   988         self.depart_docinfo_item(node)
       
   989 
       
   990     def visit_admonition(self, node, name=''):
       
   991         self.body.append('\\begin{center}\\begin{sffamily}\n')
       
   992         self.body.append('\\fbox{\\parbox{\\admonitionwidth}{\n')
       
   993         if name:
       
   994             self.body.append('\\textbf{\\large '+ self.language.labels[name] + '}\n');
       
   995         self.body.append('\\vspace{2mm}\n')
       
   996 
       
   997 
       
   998     def depart_admonition(self, node=None):
       
   999         self.body.append('}}\n') # end parbox fbox
       
  1000         self.body.append('\\end{sffamily}\n\\end{center}\n');
       
  1001 
       
  1002     def visit_attention(self, node):
       
  1003         self.visit_admonition(node, 'attention')
       
  1004 
       
  1005     def depart_attention(self, node):
       
  1006         self.depart_admonition()
       
  1007 
       
  1008     def visit_author(self, node):
       
  1009         self.visit_docinfo_item(node, 'author')
       
  1010 
       
  1011     def depart_author(self, node):
       
  1012         self.depart_docinfo_item(node)
       
  1013 
       
  1014     def visit_authors(self, node):
       
  1015         # not used: visit_author is called anyway for each author.
       
  1016         pass
       
  1017 
       
  1018     def depart_authors(self, node):
       
  1019         pass
       
  1020 
       
  1021     def visit_block_quote(self, node):
       
  1022         self.body.append( '\\begin{quote}\n')
       
  1023 
       
  1024     def depart_block_quote(self, node):
       
  1025         self.body.append( '\\end{quote}\n')
       
  1026 
       
  1027     def visit_bullet_list(self, node):
       
  1028         if 'contents' in self.topic_classes:
       
  1029             if self.use_latex_toc:
       
  1030                 raise nodes.SkipNode
       
  1031             self.body.append( '\\begin{list}{}{}\n' )
       
  1032         else:
       
  1033             self.body.append( '\\begin{itemize}\n' )
       
  1034 
       
  1035     def depart_bullet_list(self, node):
       
  1036         if 'contents' in self.topic_classes:
       
  1037             self.body.append( '\\end{list}\n' )
       
  1038         else:
       
  1039             self.body.append( '\\end{itemize}\n' )
       
  1040 
       
  1041     # Imperfect superscript/subscript handling: mathmode italicizes
       
  1042     # all letters by default.
       
  1043     def visit_superscript(self, node):
       
  1044         self.body.append('$^{')
       
  1045         self.mathmode = 1
       
  1046 
       
  1047     def depart_superscript(self, node):
       
  1048         self.body.append('}$')
       
  1049         self.mathmode = 0
       
  1050 
       
  1051     def visit_subscript(self, node):
       
  1052         self.body.append('$_{')
       
  1053         self.mathmode = 1
       
  1054 
       
  1055     def depart_subscript(self, node):
       
  1056         self.body.append('}$')
       
  1057         self.mathmode = 0
       
  1058 
       
  1059     def visit_caption(self, node):
       
  1060         self.body.append( '\\caption{' )
       
  1061 
       
  1062     def depart_caption(self, node):
       
  1063         self.body.append('}')
       
  1064 
       
  1065     def visit_caution(self, node):
       
  1066         self.visit_admonition(node, 'caution')
       
  1067 
       
  1068     def depart_caution(self, node):
       
  1069         self.depart_admonition()
       
  1070 
       
  1071     def visit_title_reference(self, node):
       
  1072         self.body.append( '\\titlereference{' )
       
  1073 
       
  1074     def depart_title_reference(self, node):
       
  1075         self.body.append( '}' )
       
  1076 
       
  1077     def visit_citation(self, node):
       
  1078         # TODO maybe use cite bibitems
       
  1079         if self._use_latex_citations:
       
  1080             self.context.append(len(self.body))
       
  1081         else:
       
  1082             self.body.append('\\begin{figure}[b]')
       
  1083             for id in node['ids']:
       
  1084                 self.body.append('\\hypertarget{%s}' % id)
       
  1085 
       
  1086     def depart_citation(self, node):
       
  1087         if self._use_latex_citations:
       
  1088             size = self.context.pop()
       
  1089             label = self.body[size]
       
  1090             text = ''.join(self.body[size+1:])
       
  1091             del self.body[size:]
       
  1092             self._bibitems.append([label, text])
       
  1093         else:
       
  1094             self.body.append('\\end{figure}\n')
       
  1095 
       
  1096     def visit_citation_reference(self, node):
       
  1097         if self._use_latex_citations:
       
  1098             self.body.append('\\cite{')
       
  1099             self.inside_citation_reference_label = 1
       
  1100         else:
       
  1101             href = ''
       
  1102             if node.has_key('refid'):
       
  1103                 href = node['refid']
       
  1104             elif node.has_key('refname'):
       
  1105                 href = self.document.nameids[node['refname']]
       
  1106             self.body.append('[\\hyperlink{%s}{' % href)
       
  1107 
       
  1108     def depart_citation_reference(self, node):
       
  1109         if self._use_latex_citations:
       
  1110             self.body.append('}')
       
  1111             self.inside_citation_reference_label = 0
       
  1112         else:
       
  1113             self.body.append('}]')
       
  1114 
       
  1115     def visit_classifier(self, node):
       
  1116         self.body.append( '(\\textbf{' )
       
  1117 
       
  1118     def depart_classifier(self, node):
       
  1119         self.body.append( '})\n' )
       
  1120 
       
  1121     def visit_colspec(self, node):
       
  1122         self.active_table.visit_colspec(node)
       
  1123 
       
  1124     def depart_colspec(self, node):
       
  1125         pass
       
  1126 
       
  1127     def visit_comment(self, node):
       
  1128         # Escape end of line by a new comment start in comment text.
       
  1129         self.body.append('%% %s \n' % node.astext().replace('\n', '\n% '))
       
  1130         raise nodes.SkipNode
       
  1131 
       
  1132     def visit_compound(self, node):
       
  1133         pass
       
  1134 
       
  1135     def depart_compound(self, node):
       
  1136         pass
       
  1137 
       
  1138     def visit_contact(self, node):
       
  1139         self.visit_docinfo_item(node, 'contact')
       
  1140 
       
  1141     def depart_contact(self, node):
       
  1142         self.depart_docinfo_item(node)
       
  1143 
       
  1144     def visit_container(self, node):
       
  1145         pass
       
  1146 
       
  1147     def depart_container(self, node):
       
  1148         pass
       
  1149 
       
  1150     def visit_copyright(self, node):
       
  1151         self.visit_docinfo_item(node, 'copyright')
       
  1152 
       
  1153     def depart_copyright(self, node):
       
  1154         self.depart_docinfo_item(node)
       
  1155 
       
  1156     def visit_danger(self, node):
       
  1157         self.visit_admonition(node, 'danger')
       
  1158 
       
  1159     def depart_danger(self, node):
       
  1160         self.depart_admonition()
       
  1161 
       
  1162     def visit_date(self, node):
       
  1163         self.visit_docinfo_item(node, 'date')
       
  1164 
       
  1165     def depart_date(self, node):
       
  1166         self.depart_docinfo_item(node)
       
  1167 
       
  1168     def visit_decoration(self, node):
       
  1169         pass
       
  1170 
       
  1171     def depart_decoration(self, node):
       
  1172         pass
       
  1173 
       
  1174     def visit_definition(self, node):
       
  1175         pass
       
  1176 
       
  1177     def depart_definition(self, node):
       
  1178         self.body.append('\n')
       
  1179 
       
  1180     def visit_definition_list(self, node):
       
  1181         self.body.append( '\\begin{description}\n' )
       
  1182 
       
  1183     def depart_definition_list(self, node):
       
  1184         self.body.append( '\\end{description}\n' )
       
  1185 
       
  1186     def visit_definition_list_item(self, node):
       
  1187         pass
       
  1188 
       
  1189     def depart_definition_list_item(self, node):
       
  1190         pass
       
  1191 
       
  1192     def visit_description(self, node):
       
  1193         self.body.append( ' ' )
       
  1194 
       
  1195     def depart_description(self, node):
       
  1196         pass
       
  1197 
       
  1198     def visit_docinfo(self, node):
       
  1199         self.docinfo = []
       
  1200         self.docinfo.append('%' + '_'*75 + '\n')
       
  1201         self.docinfo.append('\\begin{center}\n')
       
  1202         self.docinfo.append('\\begin{tabularx}{\\docinfowidth}{lX}\n')
       
  1203 
       
  1204     def depart_docinfo(self, node):
       
  1205         self.docinfo.append('\\end{tabularx}\n')
       
  1206         self.docinfo.append('\\end{center}\n')
       
  1207         self.body = self.docinfo + self.body
       
  1208         # clear docinfo, so field names are no longer appended.
       
  1209         self.docinfo = None
       
  1210 
       
  1211     def visit_docinfo_item(self, node, name):
       
  1212         if name == 'author':
       
  1213             if not self.pdfinfo == None:
       
  1214                 if not self.pdfauthor:
       
  1215                     self.pdfauthor = self.attval(node.astext())
       
  1216                 else:
       
  1217                     self.pdfauthor += self.author_separator + self.attval(node.astext())
       
  1218         if self.use_latex_docinfo:
       
  1219             if name in ('author', 'organization', 'contact', 'address'):
       
  1220                 # We attach these to the last author.  If any of them precedes
       
  1221                 # the first author, put them in a separate "author" group (for
       
  1222                 # no better semantics).
       
  1223                 if name == 'author' or not self.author_stack:
       
  1224                     self.author_stack.append([])
       
  1225                 if name == 'address':   # newlines are meaningful
       
  1226                     self.insert_newline = 1
       
  1227                     text = self.encode(node.astext())
       
  1228                     self.insert_newline = 0
       
  1229                 else:
       
  1230                     text = self.attval(node.astext())
       
  1231                 self.author_stack[-1].append(text)
       
  1232                 raise nodes.SkipNode
       
  1233             elif name == 'date':
       
  1234                 self.date = self.attval(node.astext())
       
  1235                 raise nodes.SkipNode
       
  1236         self.docinfo.append('\\textbf{%s}: &\n\t' % self.language_label(name))
       
  1237         if name == 'address':
       
  1238             self.insert_newline = 1
       
  1239             self.docinfo.append('{\\raggedright\n')
       
  1240             self.context.append(' } \\\\\n')
       
  1241         else:
       
  1242             self.context.append(' \\\\\n')
       
  1243         self.context.append(self.docinfo)
       
  1244         self.context.append(len(self.body))
       
  1245 
       
  1246     def depart_docinfo_item(self, node):
       
  1247         size = self.context.pop()
       
  1248         dest = self.context.pop()
       
  1249         tail = self.context.pop()
       
  1250         tail = self.body[size:] + [tail]
       
  1251         del self.body[size:]
       
  1252         dest.extend(tail)
       
  1253         # for address we did set insert_newline
       
  1254         self.insert_newline = 0
       
  1255 
       
  1256     def visit_doctest_block(self, node):
       
  1257         self.body.append( '\\begin{verbatim}' )
       
  1258         self.verbatim = 1
       
  1259 
       
  1260     def depart_doctest_block(self, node):
       
  1261         self.body.append( '\\end{verbatim}\n' )
       
  1262         self.verbatim = 0
       
  1263 
       
  1264     def visit_document(self, node):
       
  1265         self.body_prefix.append('\\begin{document}\n')
       
  1266         # titled document?
       
  1267         if self.use_latex_docinfo or len(node) and isinstance(node[0], nodes.title):
       
  1268             self.body_prefix.append('\\maketitle\n')
       
  1269             # alternative use titlepage environment.
       
  1270             # \begin{titlepage}
       
  1271             # ...
       
  1272         self.body.append('\n\\setlength{\\locallinewidth}{\\linewidth}\n')
       
  1273 
       
  1274     def depart_document(self, node):
       
  1275         # TODO insertion point of bibliography should none automatic.
       
  1276         if self._use_latex_citations and len(self._bibitems)>0:
       
  1277             if not self.bibtex:
       
  1278                 widest_label = ""
       
  1279                 for bi in self._bibitems:
       
  1280                     if len(widest_label)<len(bi[0]):
       
  1281                         widest_label = bi[0]
       
  1282                 self.body.append('\n\\begin{thebibliography}{%s}\n'%widest_label)
       
  1283                 for bi in self._bibitems:
       
  1284                     # cite_key: underscores must not be escaped
       
  1285                     cite_key = bi[0].replace(r"{\_}","_")
       
  1286                     self.body.append('\\bibitem[%s]{%s}{%s}\n' % (bi[0], cite_key, bi[1]))
       
  1287                 self.body.append('\\end{thebibliography}\n')
       
  1288             else:
       
  1289                 self.body.append('\n\\bibliographystyle{%s}\n' % self.bibtex[0])
       
  1290                 self.body.append('\\bibliography{%s}\n' % self.bibtex[1])
       
  1291 
       
  1292         self.body_suffix.append('\\end{document}\n')
       
  1293 
       
  1294     def visit_emphasis(self, node):
       
  1295         self.body.append('\\emph{')
       
  1296         self.literal_block_stack.append('\\emph{')
       
  1297 
       
  1298     def depart_emphasis(self, node):
       
  1299         self.body.append('}')
       
  1300         self.literal_block_stack.pop()
       
  1301 
       
  1302     def visit_entry(self, node):
       
  1303         self.active_table.visit_entry()
       
  1304         # cell separation
       
  1305         if self.active_table.get_entry_number() == 1:
       
  1306             # if the firstrow is a multirow, this actually is the second row.
       
  1307             # this gets hairy if rowspans follow each other.
       
  1308             if self.active_table.get_rowspan(0):
       
  1309                 count = 0
       
  1310                 while self.active_table.get_rowspan(count):
       
  1311                     count += 1
       
  1312                     self.body.append(' & ')
       
  1313                 self.active_table.visit_entry() # increment cell count
       
  1314         else:
       
  1315             self.body.append(' & ')
       
  1316 
       
  1317         # multi{row,column}
       
  1318         # IN WORK BUG TODO HACK continues here
       
  1319         # multirow in LaTeX simply will enlarge the cell over several rows
       
  1320         # (the following n if n is positive, the former if negative).
       
  1321         if node.has_key('morerows') and node.has_key('morecols'):
       
  1322             raise NotImplementedError('Cells that '
       
  1323             'span multiple rows *and* columns are not supported, sorry.')
       
  1324         if node.has_key('morerows'):
       
  1325             count = node['morerows'] + 1
       
  1326             self.active_table.set_rowspan(self.active_table.get_entry_number()-1,count)
       
  1327             self.body.append('\\multirow{%d}{%s}{' % \
       
  1328                     (count,self.active_table.get_column_width()))
       
  1329             self.context.append('}')
       
  1330             # BUG following rows must have empty cells.
       
  1331         elif node.has_key('morecols'):
       
  1332             # the vertical bar before column is missing if it is the first column.
       
  1333             # the one after always.
       
  1334             if self.active_table.get_entry_number() == 1:
       
  1335                 bar1 = self.active_table.get_vertical_bar()
       
  1336             else:
       
  1337                 bar1 = ''
       
  1338             count = node['morecols'] + 1
       
  1339             self.body.append('\\multicolumn{%d}{%sl%s}{' % \
       
  1340                     (count, bar1, self.active_table.get_vertical_bar()))
       
  1341             self.context.append('}')
       
  1342         else:
       
  1343             self.context.append('')
       
  1344 
       
  1345         # header / not header
       
  1346         if isinstance(node.parent.parent, nodes.thead):
       
  1347             self.body.append('\\textbf{')
       
  1348             self.context.append('}')
       
  1349         elif self.active_table.is_stub_column():
       
  1350             self.body.append('\\textbf{')
       
  1351             self.context.append('}')
       
  1352         else:
       
  1353             self.context.append('')
       
  1354 
       
  1355     def depart_entry(self, node):
       
  1356         self.body.append(self.context.pop()) # header / not header
       
  1357         self.body.append(self.context.pop()) # multirow/column
       
  1358         # if following row is spanned from above.
       
  1359         if self.active_table.get_rowspan(self.active_table.get_entry_number()):
       
  1360            self.body.append(' & ')
       
  1361            self.active_table.visit_entry() # increment cell count
       
  1362 
       
  1363     def visit_row(self, node):
       
  1364         self.active_table.visit_row()
       
  1365 
       
  1366     def depart_row(self, node):
       
  1367         self.body.extend(self.active_table.depart_row())
       
  1368 
       
  1369     def visit_enumerated_list(self, node):
       
  1370         # We create our own enumeration list environment.
       
  1371         # This allows to set the style and starting value
       
  1372         # and unlimited nesting.
       
  1373         enum_style = {'arabic':'arabic',
       
  1374                 'loweralpha':'alph',
       
  1375                 'upperalpha':'Alph',
       
  1376                 'lowerroman':'roman',
       
  1377                 'upperroman':'Roman' }
       
  1378         enum_suffix = ""
       
  1379         if node.has_key('suffix'):
       
  1380             enum_suffix = node['suffix']
       
  1381         enum_prefix = ""
       
  1382         if node.has_key('prefix'):
       
  1383             enum_prefix = node['prefix']
       
  1384         if self.compound_enumerators:
       
  1385             pref = ""
       
  1386             if self.section_prefix_for_enumerators and self.section_level:
       
  1387                 for i in range(self.section_level):
       
  1388                     pref += '%d.' % self._section_number[i]
       
  1389                 pref = pref[:-1] + self.section_enumerator_separator
       
  1390                 enum_prefix += pref
       
  1391             for ctype, cname in self._enumeration_counters:
       
  1392                 enum_prefix += '\\%s{%s}.' % (ctype, cname)
       
  1393         enum_type = "arabic"
       
  1394         if node.has_key('enumtype'):
       
  1395             enum_type = node['enumtype']
       
  1396         if enum_style.has_key(enum_type):
       
  1397             enum_type = enum_style[enum_type]
       
  1398 
       
  1399         counter_name = "listcnt%d" % len(self._enumeration_counters)
       
  1400         self._enumeration_counters.append((enum_type, counter_name))
       
  1401         # If we haven't used this counter name before, then create a
       
  1402         # new counter; otherwise, reset & reuse the old counter.
       
  1403         if len(self._enumeration_counters) > self._max_enumeration_counters:
       
  1404             self._max_enumeration_counters = len(self._enumeration_counters)
       
  1405             self.body.append('\\newcounter{%s}\n' % counter_name)
       
  1406         else:
       
  1407             self.body.append('\\setcounter{%s}{0}\n' % counter_name)
       
  1408             
       
  1409         self.body.append('\\begin{list}{%s\\%s{%s}%s}\n' % \
       
  1410             (enum_prefix,enum_type,counter_name,enum_suffix))
       
  1411         self.body.append('{\n')
       
  1412         self.body.append('\\usecounter{%s}\n' % counter_name)
       
  1413         # set start after usecounter, because it initializes to zero.
       
  1414         if node.has_key('start'):
       
  1415             self.body.append('\\addtocounter{%s}{%d}\n' \
       
  1416                     % (counter_name,node['start']-1))
       
  1417         ## set rightmargin equal to leftmargin
       
  1418         self.body.append('\\setlength{\\rightmargin}{\\leftmargin}\n')
       
  1419         self.body.append('}\n')
       
  1420 
       
  1421     def depart_enumerated_list(self, node):
       
  1422         self.body.append('\\end{list}\n')
       
  1423         self._enumeration_counters.pop()
       
  1424 
       
  1425     def visit_error(self, node):
       
  1426         self.visit_admonition(node, 'error')
       
  1427 
       
  1428     def depart_error(self, node):
       
  1429         self.depart_admonition()
       
  1430 
       
  1431     def visit_field(self, node):
       
  1432         # real output is done in siblings: _argument, _body, _name
       
  1433         pass
       
  1434 
       
  1435     def depart_field(self, node):
       
  1436         self.body.append('\n')
       
  1437         ##self.body.append('%[depart_field]\n')
       
  1438 
       
  1439     def visit_field_argument(self, node):
       
  1440         self.body.append('%[visit_field_argument]\n')
       
  1441 
       
  1442     def depart_field_argument(self, node):
       
  1443         self.body.append('%[depart_field_argument]\n')
       
  1444 
       
  1445     def visit_field_body(self, node):
       
  1446         # BUG by attach as text we loose references.
       
  1447         if self.docinfo:
       
  1448             self.docinfo.append('%s \\\\\n' % self.encode(node.astext()))
       
  1449             raise nodes.SkipNode
       
  1450         # BUG: what happens if not docinfo
       
  1451 
       
  1452     def depart_field_body(self, node):
       
  1453         self.body.append( '\n' )
       
  1454 
       
  1455     def visit_field_list(self, node):
       
  1456         if not self.docinfo:
       
  1457             self.body.append('\\begin{quote}\n')
       
  1458             self.body.append('\\begin{description}\n')
       
  1459 
       
  1460     def depart_field_list(self, node):
       
  1461         if not self.docinfo:
       
  1462             self.body.append('\\end{description}\n')
       
  1463             self.body.append('\\end{quote}\n')
       
  1464 
       
  1465     def visit_field_name(self, node):
       
  1466         # BUG this duplicates docinfo_item
       
  1467         if self.docinfo:
       
  1468             self.docinfo.append('\\textbf{%s}: &\n\t' % self.encode(node.astext()))
       
  1469             raise nodes.SkipNode
       
  1470         else:
       
  1471             self.body.append('\\item [')
       
  1472 
       
  1473     def depart_field_name(self, node):
       
  1474         if not self.docinfo:
       
  1475             self.body.append(':]')
       
  1476 
       
  1477     def visit_figure(self, node):
       
  1478         if (not node.attributes.has_key('align') or
       
  1479             node.attributes['align'] == 'center'):
       
  1480             # centering does not add vertical space like center.
       
  1481             align = '\n\\centering'
       
  1482             align_end = ''
       
  1483         else:
       
  1484             # TODO non vertical space for other alignments.
       
  1485             align = '\\begin{flush%s}' % node.attributes['align']
       
  1486             align_end = '\\end{flush%s}' % node.attributes['align']
       
  1487         self.body.append( '\\begin{figure}[htbp]%s\n' % align )
       
  1488         self.context.append( '%s\\end{figure}\n' % align_end )
       
  1489 
       
  1490     def depart_figure(self, node):
       
  1491         self.body.append( self.context.pop() )
       
  1492 
       
  1493     def visit_footer(self, node):
       
  1494         self.context.append(len(self.body))
       
  1495 
       
  1496     def depart_footer(self, node):
       
  1497         start = self.context.pop()
       
  1498         footer = (['\n\\begin{center}\small\n']
       
  1499                   + self.body[start:] + ['\n\\end{center}\n'])
       
  1500         self.body_suffix[:0] = footer
       
  1501         del self.body[start:]
       
  1502 
       
  1503     def visit_footnote(self, node):
       
  1504         if self.use_latex_footnotes:
       
  1505             num,text = node.astext().split(None,1)
       
  1506             num = self.encode(num.strip())
       
  1507             self.body.append('\\footnotetext['+num+']')
       
  1508             self.body.append('{')
       
  1509         else:
       
  1510             self.body.append('\\begin{figure}[b]')
       
  1511             for id in node['ids']:
       
  1512                 self.body.append('\\hypertarget{%s}' % id)
       
  1513 
       
  1514     def depart_footnote(self, node):
       
  1515         if self.use_latex_footnotes:
       
  1516             self.body.append('}\n')
       
  1517         else:
       
  1518             self.body.append('\\end{figure}\n')
       
  1519 
       
  1520     def visit_footnote_reference(self, node):
       
  1521         if self.use_latex_footnotes:
       
  1522             self.body.append("\\footnotemark["+self.encode(node.astext())+"]")
       
  1523             raise nodes.SkipNode
       
  1524         href = ''
       
  1525         if node.has_key('refid'):
       
  1526             href = node['refid']
       
  1527         elif node.has_key('refname'):
       
  1528             href = self.document.nameids[node['refname']]
       
  1529         format = self.settings.footnote_references
       
  1530         if format == 'brackets':
       
  1531             suffix = '['
       
  1532             self.context.append(']')
       
  1533         elif format == 'superscript':
       
  1534             suffix = '\\raisebox{.5em}[0em]{\\scriptsize'
       
  1535             self.context.append('}')
       
  1536         else:                           # shouldn't happen
       
  1537             raise AssertionError('Illegal footnote reference format.')
       
  1538         self.body.append('%s\\hyperlink{%s}{' % (suffix,href))
       
  1539 
       
  1540     def depart_footnote_reference(self, node):
       
  1541         if self.use_latex_footnotes:
       
  1542             return
       
  1543         self.body.append('}%s' % self.context.pop())
       
  1544 
       
  1545     # footnote/citation label
       
  1546     def label_delim(self, node, bracket, superscript):
       
  1547         if isinstance(node.parent, nodes.footnote):
       
  1548             if self.use_latex_footnotes:
       
  1549                 raise nodes.SkipNode
       
  1550             if self.settings.footnote_references == 'brackets':
       
  1551                 self.body.append(bracket)
       
  1552             else:
       
  1553                 self.body.append(superscript)
       
  1554         else:
       
  1555             assert isinstance(node.parent, nodes.citation)
       
  1556             if not self._use_latex_citations:
       
  1557                 self.body.append(bracket)
       
  1558 
       
  1559     def visit_label(self, node):
       
  1560         self.label_delim(node, '[', '$^{')
       
  1561 
       
  1562     def depart_label(self, node):
       
  1563         self.label_delim(node, ']', '}$')
       
  1564 
       
  1565     # elements generated by the framework e.g. section numbers.
       
  1566     def visit_generated(self, node):
       
  1567         pass
       
  1568 
       
  1569     def depart_generated(self, node):
       
  1570         pass
       
  1571 
       
  1572     def visit_header(self, node):
       
  1573         self.context.append(len(self.body))
       
  1574 
       
  1575     def depart_header(self, node):
       
  1576         start = self.context.pop()
       
  1577         self.body_prefix.append('\n\\verb|begin_header|\n')
       
  1578         self.body_prefix.extend(self.body[start:])
       
  1579         self.body_prefix.append('\n\\verb|end_header|\n')
       
  1580         del self.body[start:]
       
  1581 
       
  1582     def visit_hint(self, node):
       
  1583         self.visit_admonition(node, 'hint')
       
  1584 
       
  1585     def depart_hint(self, node):
       
  1586         self.depart_admonition()
       
  1587 
       
  1588     def latex_image_length(self, width_str):
       
  1589         match = re.match('(\d*\.?\d*)\s*(\S*)', width_str)
       
  1590         if not match:
       
  1591             # fallback
       
  1592             return width_str
       
  1593         res = width_str
       
  1594         amount, unit = match.groups()[:2]
       
  1595         if unit == "px":
       
  1596             # LaTeX does not know pixels but points
       
  1597             res = "%spt" % amount
       
  1598         elif unit == "%":
       
  1599             res = "%.3f\\linewidth" % (float(amount)/100.0)
       
  1600         return res
       
  1601 
       
  1602     def visit_image(self, node):
       
  1603         attrs = node.attributes
       
  1604         # Add image URI to dependency list, assuming that it's
       
  1605         # referring to a local file.
       
  1606         self.settings.record_dependencies.add(attrs['uri'])
       
  1607         pre = []                        # in reverse order
       
  1608         post = []
       
  1609         include_graphics_options = []
       
  1610         inline = isinstance(node.parent, nodes.TextElement)
       
  1611         if attrs.has_key('scale'):
       
  1612             # Could also be done with ``scale`` option to
       
  1613             # ``\includegraphics``; doing it this way for consistency.
       
  1614             pre.append('\\scalebox{%f}{' % (attrs['scale'] / 100.0,))
       
  1615             post.append('}')
       
  1616         if attrs.has_key('width'):
       
  1617             include_graphics_options.append('width=%s' % (
       
  1618                             self.latex_image_length(attrs['width']), ))
       
  1619         if attrs.has_key('height'):
       
  1620             include_graphics_options.append('height=%s' % (
       
  1621                             self.latex_image_length(attrs['height']), ))
       
  1622         if attrs.has_key('align'):
       
  1623             align_prepost = {
       
  1624                 # By default latex aligns the top of an image.
       
  1625                 (1, 'top'): ('', ''),
       
  1626                 (1, 'middle'): ('\\raisebox{-0.5\\height}{', '}'),
       
  1627                 (1, 'bottom'): ('\\raisebox{-\\height}{', '}'),
       
  1628                 (0, 'center'): ('{\\hfill', '\\hfill}'),
       
  1629                 # These 2 don't exactly do the right thing.  The image should
       
  1630                 # be floated alongside the paragraph.  See
       
  1631                 # http://www.w3.org/TR/html4/struct/objects.html#adef-align-IMG
       
  1632                 (0, 'left'): ('{', '\\hfill}'),
       
  1633                 (0, 'right'): ('{\\hfill', '}'),}
       
  1634             try:
       
  1635                 pre.append(align_prepost[inline, attrs['align']][0])
       
  1636                 post.append(align_prepost[inline, attrs['align']][1])
       
  1637             except KeyError:
       
  1638                 pass                    # XXX complain here?
       
  1639         if not inline:
       
  1640             pre.append('\n')
       
  1641             post.append('\n')
       
  1642         pre.reverse()
       
  1643         self.body.extend( pre )
       
  1644         options = ''
       
  1645         if len(include_graphics_options)>0:
       
  1646             options = '[%s]' % (','.join(include_graphics_options))
       
  1647         self.body.append( '\\includegraphics%s{%s}' % (
       
  1648                             options, attrs['uri'] ) )
       
  1649         self.body.extend( post )
       
  1650 
       
  1651     def depart_image(self, node):
       
  1652         pass
       
  1653 
       
  1654     def visit_important(self, node):
       
  1655         self.visit_admonition(node, 'important')
       
  1656 
       
  1657     def depart_important(self, node):
       
  1658         self.depart_admonition()
       
  1659 
       
  1660     def visit_interpreted(self, node):
       
  1661         # @@@ Incomplete, pending a proper implementation on the
       
  1662         # Parser/Reader end.
       
  1663         self.visit_literal(node)
       
  1664 
       
  1665     def depart_interpreted(self, node):
       
  1666         self.depart_literal(node)
       
  1667 
       
  1668     def visit_legend(self, node):
       
  1669         self.body.append('{\\small ')
       
  1670 
       
  1671     def depart_legend(self, node):
       
  1672         self.body.append('}')
       
  1673 
       
  1674     def visit_line(self, node):
       
  1675         self.body.append('\item[] ')
       
  1676 
       
  1677     def depart_line(self, node):
       
  1678         self.body.append('\n')
       
  1679 
       
  1680     def visit_line_block(self, node):
       
  1681         if isinstance(node.parent, nodes.line_block):
       
  1682             self.body.append('\\item[] \n'
       
  1683                              '\\begin{lineblock}{\\lineblockindentation}\n')
       
  1684         else:
       
  1685             self.body.append('\n\\begin{lineblock}{0em}\n')
       
  1686 
       
  1687     def depart_line_block(self, node):
       
  1688         self.body.append('\\end{lineblock}\n')
       
  1689 
       
  1690     def visit_list_item(self, node):
       
  1691         # Append "{}" in case the next character is "[", which would break
       
  1692         # LaTeX's list environment (no numbering and the "[" is not printed).
       
  1693         self.body.append('\\item {} ')
       
  1694 
       
  1695     def depart_list_item(self, node):
       
  1696         self.body.append('\n')
       
  1697 
       
  1698     def visit_literal(self, node):
       
  1699         self.literal = 1
       
  1700         self.body.append('\\texttt{')
       
  1701 
       
  1702     def depart_literal(self, node):
       
  1703         self.body.append('}')
       
  1704         self.literal = 0
       
  1705 
       
  1706     def visit_literal_block(self, node):
       
  1707         """
       
  1708         Render a literal-block.
       
  1709 
       
  1710         Literal blocks are used for "::"-prefixed literal-indented
       
  1711         blocks of text, where the inline markup is not recognized,
       
  1712         but are also the product of the parsed-literal directive,
       
  1713         where the markup is respected.
       
  1714         """
       
  1715         # In both cases, we want to use a typewriter/monospaced typeface.
       
  1716         # For "real" literal-blocks, we can use \verbatim, while for all
       
  1717         # the others we must use \mbox.
       
  1718         #
       
  1719         # We can distinguish between the two kinds by the number of
       
  1720         # siblings that compose this node: if it is composed by a
       
  1721         # single element, it's surely either a real one or a
       
  1722         # parsed-literal that does not contain any markup.
       
  1723         #
       
  1724         if not self.active_table.is_open():
       
  1725             # no quote inside tables, to avoid vertical space between
       
  1726             # table border and literal block.
       
  1727             # BUG: fails if normal text preceeds the literal block.
       
  1728             self.body.append('\\begin{quote}')
       
  1729             self.context.append('\\end{quote}\n')
       
  1730         else:
       
  1731             self.body.append('\n')
       
  1732             self.context.append('\n')
       
  1733         if (self.settings.use_verbatim_when_possible and (len(node) == 1)
       
  1734               # in case of a parsed-literal containing just a "**bold**" word:
       
  1735               and isinstance(node[0], nodes.Text)):
       
  1736             self.verbatim = 1
       
  1737             self.body.append('\\begin{verbatim}\n')
       
  1738         else:
       
  1739             self.literal_block = 1
       
  1740             self.insert_none_breaking_blanks = 1
       
  1741             self.body.append('{\\ttfamily \\raggedright \\noindent\n')
       
  1742             # * obey..: is from julien and never worked for me (grubert).
       
  1743             #   self.body.append('{\\obeylines\\obeyspaces\\ttfamily\n')
       
  1744 
       
  1745     def depart_literal_block(self, node):
       
  1746         if self.verbatim:
       
  1747             self.body.append('\n\\end{verbatim}\n')
       
  1748             self.verbatim = 0
       
  1749         else:
       
  1750             self.body.append('\n}')
       
  1751             self.insert_none_breaking_blanks = 0
       
  1752             self.literal_block = 0
       
  1753             # obey end: self.body.append('}\n')
       
  1754         self.body.append(self.context.pop())
       
  1755 
       
  1756     def visit_meta(self, node):
       
  1757         self.body.append('[visit_meta]\n')
       
  1758         # BUG maybe set keywords for pdf
       
  1759         ##self.head.append(self.starttag(node, 'meta', **node.attributes))
       
  1760 
       
  1761     def depart_meta(self, node):
       
  1762         self.body.append('[depart_meta]\n')
       
  1763 
       
  1764     def visit_note(self, node):
       
  1765         self.visit_admonition(node, 'note')
       
  1766 
       
  1767     def depart_note(self, node):
       
  1768         self.depart_admonition()
       
  1769 
       
  1770     def visit_option(self, node):
       
  1771         if self.context[-1]:
       
  1772             # this is not the first option
       
  1773             self.body.append(', ')
       
  1774 
       
  1775     def depart_option(self, node):
       
  1776         # flag tha the first option is done.
       
  1777         self.context[-1] += 1
       
  1778 
       
  1779     def visit_option_argument(self, node):
       
  1780         """The delimiter betweeen an option and its argument."""
       
  1781         self.body.append(node.get('delimiter', ' '))
       
  1782 
       
  1783     def depart_option_argument(self, node):
       
  1784         pass
       
  1785 
       
  1786     def visit_option_group(self, node):
       
  1787         self.body.append('\\item [')
       
  1788         # flag for first option
       
  1789         self.context.append(0)
       
  1790 
       
  1791     def depart_option_group(self, node):
       
  1792         self.context.pop() # the flag
       
  1793         self.body.append('] ')
       
  1794 
       
  1795     def visit_option_list(self, node):
       
  1796         self.body.append('\\begin{optionlist}{3cm}\n')
       
  1797 
       
  1798     def depart_option_list(self, node):
       
  1799         self.body.append('\\end{optionlist}\n')
       
  1800 
       
  1801     def visit_option_list_item(self, node):
       
  1802         pass
       
  1803 
       
  1804     def depart_option_list_item(self, node):
       
  1805         pass
       
  1806 
       
  1807     def visit_option_string(self, node):
       
  1808         ##self.body.append(self.starttag(node, 'span', '', CLASS='option'))
       
  1809         pass
       
  1810 
       
  1811     def depart_option_string(self, node):
       
  1812         ##self.body.append('</span>')
       
  1813         pass
       
  1814 
       
  1815     def visit_organization(self, node):
       
  1816         self.visit_docinfo_item(node, 'organization')
       
  1817 
       
  1818     def depart_organization(self, node):
       
  1819         self.depart_docinfo_item(node)
       
  1820 
       
  1821     def visit_paragraph(self, node):
       
  1822         index = node.parent.index(node)
       
  1823         if not ('contents' in self.topic_classes or
       
  1824                 (isinstance(node.parent, nodes.compound) and
       
  1825                  index > 0 and
       
  1826                  not isinstance(node.parent[index - 1], nodes.paragraph) and
       
  1827                  not isinstance(node.parent[index - 1], nodes.compound))):
       
  1828             self.body.append('\n')
       
  1829 
       
  1830     def depart_paragraph(self, node):
       
  1831         self.body.append('\n')
       
  1832 
       
  1833     def visit_problematic(self, node):
       
  1834         self.body.append('{\\color{red}\\bfseries{}')
       
  1835 
       
  1836     def depart_problematic(self, node):
       
  1837         self.body.append('}')
       
  1838 
       
  1839     def visit_raw(self, node):
       
  1840         if 'latex' in node.get('format', '').split():
       
  1841             self.body.append(node.astext())
       
  1842         raise nodes.SkipNode
       
  1843 
       
  1844     def visit_reference(self, node):
       
  1845         # BUG: hash_char "#" is trouble some in LaTeX.
       
  1846         # mbox and other environment do not like the '#'.
       
  1847         hash_char = '\\#'
       
  1848         if node.has_key('refuri'):
       
  1849             href = node['refuri'].replace('#',hash_char)
       
  1850         elif node.has_key('refid'):
       
  1851             href = hash_char + node['refid']
       
  1852         elif node.has_key('refname'):
       
  1853             href = hash_char + self.document.nameids[node['refname']]
       
  1854         else:
       
  1855             raise AssertionError('Unknown reference.')
       
  1856         self.body.append('\\href{%s}{' % href)
       
  1857         if self._reference_label and not node.has_key('refuri'):
       
  1858             self.body.append('\\%s{%s}}' % (self._reference_label,
       
  1859                         href.replace(hash_char, '')))
       
  1860             raise nodes.SkipNode
       
  1861 
       
  1862     def depart_reference(self, node):
       
  1863         self.body.append('}')
       
  1864 
       
  1865     def visit_revision(self, node):
       
  1866         self.visit_docinfo_item(node, 'revision')
       
  1867 
       
  1868     def depart_revision(self, node):
       
  1869         self.depart_docinfo_item(node)
       
  1870 
       
  1871     def visit_section(self, node):
       
  1872         self.section_level += 1
       
  1873         # Initialize counter for potential subsections:
       
  1874         self._section_number.append(0)
       
  1875         # Counter for this section's level (initialized by parent section):
       
  1876         self._section_number[self.section_level - 1] += 1
       
  1877 
       
  1878     def depart_section(self, node):
       
  1879         # Remove counter for potential subsections:
       
  1880         self._section_number.pop()
       
  1881         self.section_level -= 1
       
  1882 
       
  1883     def visit_sidebar(self, node):
       
  1884         # BUG:  this is just a hack to make sidebars render something
       
  1885         self.body.append('\n\\setlength{\\locallinewidth}{0.9\\admonitionwidth}\n')
       
  1886         self.body.append('\\begin{center}\\begin{sffamily}\n')
       
  1887         self.body.append('\\fbox{\\colorbox[gray]{0.80}{\\parbox{\\admonitionwidth}{\n')
       
  1888 
       
  1889     def depart_sidebar(self, node):
       
  1890         self.body.append('}}}\n') # end parbox colorbox fbox
       
  1891         self.body.append('\\end{sffamily}\n\\end{center}\n');
       
  1892         self.body.append('\n\\setlength{\\locallinewidth}{\\linewidth}\n')
       
  1893 
       
  1894 
       
  1895     attribution_formats = {'dash': ('---', ''),
       
  1896                            'parentheses': ('(', ')'),
       
  1897                            'parens': ('(', ')'),
       
  1898                            'none': ('', '')}
       
  1899 
       
  1900     def visit_attribution(self, node):
       
  1901         prefix, suffix = self.attribution_formats[self.settings.attribution]
       
  1902         self.body.append('\n\\begin{flushright}\n')
       
  1903         self.body.append(prefix)
       
  1904         self.context.append(suffix)
       
  1905 
       
  1906     def depart_attribution(self, node):
       
  1907         self.body.append(self.context.pop() + '\n')
       
  1908         self.body.append('\\end{flushright}\n')
       
  1909 
       
  1910     def visit_status(self, node):
       
  1911         self.visit_docinfo_item(node, 'status')
       
  1912 
       
  1913     def depart_status(self, node):
       
  1914         self.depart_docinfo_item(node)
       
  1915 
       
  1916     def visit_strong(self, node):
       
  1917         self.body.append('\\textbf{')
       
  1918         self.literal_block_stack.append('\\textbf{')
       
  1919 
       
  1920     def depart_strong(self, node):
       
  1921         self.body.append('}')
       
  1922         self.literal_block_stack.pop()
       
  1923 
       
  1924     def visit_substitution_definition(self, node):
       
  1925         raise nodes.SkipNode
       
  1926 
       
  1927     def visit_substitution_reference(self, node):
       
  1928         self.unimplemented_visit(node)
       
  1929 
       
  1930     def visit_subtitle(self, node):
       
  1931         if isinstance(node.parent, nodes.sidebar):
       
  1932             self.body.append('~\\\\\n\\textbf{')
       
  1933             self.context.append('}\n\\smallskip\n')
       
  1934         elif isinstance(node.parent, nodes.document):
       
  1935             self.title = self.title + \
       
  1936                 '\\\\\n\\large{%s}\n' % self.encode(node.astext())
       
  1937             raise nodes.SkipNode
       
  1938         elif isinstance(node.parent, nodes.section):
       
  1939             self.body.append('\\textbf{')
       
  1940             self.context.append('}\\vspace{0.2cm}\n\n\\noindent ')
       
  1941 
       
  1942     def depart_subtitle(self, node):
       
  1943         self.body.append(self.context.pop())
       
  1944 
       
  1945     def visit_system_message(self, node):
       
  1946         pass
       
  1947 
       
  1948     def depart_system_message(self, node):
       
  1949         self.body.append('\n')
       
  1950 
       
  1951     def visit_table(self, node):
       
  1952         if self.active_table.is_open():
       
  1953             self.table_stack.append(self.active_table)
       
  1954             # nesting longtable does not work (e.g. 2007-04-18)
       
  1955             self.active_table = Table('tabular',self.settings.table_style)
       
  1956         self.active_table.open()
       
  1957         for cl in node['classes']:
       
  1958             self.active_table.set_table_style(cl)
       
  1959         self.body.append('\n' + self.active_table.get_opening())
       
  1960 
       
  1961     def depart_table(self, node):
       
  1962         self.body.append(self.active_table.get_closing() + '\n')
       
  1963         self.active_table.close()
       
  1964         if len(self.table_stack)>0:
       
  1965             self.active_table = self.table_stack.pop()
       
  1966         else:
       
  1967             self.active_table.set_table_style(self.settings.table_style)
       
  1968 
       
  1969     def visit_target(self, node):
       
  1970         # BUG: why not (refuri or refid or refname) means not footnote ?
       
  1971         if not (node.has_key('refuri') or node.has_key('refid')
       
  1972                 or node.has_key('refname')):
       
  1973             for id in node['ids']:
       
  1974                 self.body.append('\\hypertarget{%s}{' % id)
       
  1975             self.context.append('}' * len(node['ids']))
       
  1976         elif node.get("refid"):
       
  1977             self.body.append('\\hypertarget{%s}{' % node.get("refid"))
       
  1978             self.context.append('}')
       
  1979         else:
       
  1980             self.context.append('')
       
  1981 
       
  1982     def depart_target(self, node):
       
  1983         self.body.append(self.context.pop())
       
  1984 
       
  1985     def visit_tbody(self, node):
       
  1986         # BUG write preamble if not yet done (colspecs not [])
       
  1987         # for tables without heads.
       
  1988         if not self.active_table.get('preamble written'):
       
  1989             self.visit_thead(None)
       
  1990             # self.depart_thead(None)
       
  1991 
       
  1992     def depart_tbody(self, node):
       
  1993         pass
       
  1994 
       
  1995     def visit_term(self, node):
       
  1996         self.body.append('\\item[{')
       
  1997 
       
  1998     def depart_term(self, node):
       
  1999         # definition list term.
       
  2000         # \leavevmode results in a line break if the term is followed by a item list.
       
  2001         self.body.append('}] \leavevmode ')
       
  2002 
       
  2003     def visit_tgroup(self, node):
       
  2004         #self.body.append(self.starttag(node, 'colgroup'))
       
  2005         #self.context.append('</colgroup>\n')
       
  2006         pass
       
  2007 
       
  2008     def depart_tgroup(self, node):
       
  2009         pass
       
  2010 
       
  2011     def visit_thead(self, node):
       
  2012         self.body.append('{%s}\n' % self.active_table.get_colspecs())
       
  2013         if self.active_table.caption:
       
  2014             self.body.append('\\caption{%s}\\\\\n' % self.active_table.caption)
       
  2015         self.active_table.set('preamble written',1)
       
  2016         # TODO longtable supports firsthead and lastfoot too.
       
  2017         self.body.extend(self.active_table.visit_thead())
       
  2018 
       
  2019     def depart_thead(self, node):
       
  2020         # the table header written should be on every page
       
  2021         # => \endhead
       
  2022         self.body.extend(self.active_table.depart_thead())
       
  2023         # and the firsthead => \endfirsthead
       
  2024         # BUG i want a "continued from previous page" on every not
       
  2025         # firsthead, but then we need the header twice.
       
  2026         #
       
  2027         # there is a \endfoot and \endlastfoot too.
       
  2028         # but we need the number of columns to
       
  2029         # self.body.append('\\multicolumn{%d}{c}{"..."}\n' % number_of_columns)
       
  2030         # self.body.append('\\hline\n\\endfoot\n')
       
  2031         # self.body.append('\\hline\n')
       
  2032         # self.body.append('\\endlastfoot\n')
       
  2033 
       
  2034     def visit_tip(self, node):
       
  2035         self.visit_admonition(node, 'tip')
       
  2036 
       
  2037     def depart_tip(self, node):
       
  2038         self.depart_admonition()
       
  2039 
       
  2040     def bookmark(self, node):
       
  2041         """Append latex href and pdfbookmarks for titles.
       
  2042         """
       
  2043         if node.parent['ids']:
       
  2044             for id in node.parent['ids']:
       
  2045                 self.body.append('\\hypertarget{%s}{}\n' % id)
       
  2046             if not self.use_latex_toc:
       
  2047                 # BUG level depends on style. pdflatex allows level 0 to 3
       
  2048                 # ToC would be the only on level 0 so i choose to decrement the rest.
       
  2049                 # "Table of contents" bookmark to see the ToC. To avoid this
       
  2050                 # we set all zeroes to one.
       
  2051                 l = self.section_level
       
  2052                 if l>0:
       
  2053                     l = l-1
       
  2054                 # pdftex does not like "_" subscripts in titles
       
  2055                 text = self.encode(node.astext())
       
  2056                 for id in node.parent['ids']:
       
  2057                     self.body.append('\\pdfbookmark[%d]{%s}{%s}\n' % \
       
  2058                                      (l, text, id))
       
  2059 
       
  2060     def visit_title(self, node):
       
  2061         """Section and other titles."""
       
  2062 
       
  2063         if isinstance(node.parent, nodes.topic):
       
  2064             # the table of contents.
       
  2065             self.bookmark(node)
       
  2066             if ('contents' in self.topic_classes
       
  2067             and self.use_latex_toc):
       
  2068                 self.body.append('\\renewcommand{\\contentsname}{')
       
  2069                 self.context.append('}\n\\tableofcontents\n\n\\bigskip\n')
       
  2070             elif ('abstract' in self.topic_classes
       
  2071             and self.settings.use_latex_abstract):
       
  2072                 raise nodes.SkipNode
       
  2073             else: # or section titles before the table of contents.
       
  2074                 # BUG: latex chokes on center environment with 
       
  2075                 # "perhaps a missing item", therefore we use hfill.
       
  2076                 self.body.append('\\subsubsection*{~\\hfill ')
       
  2077                 # the closing brace for subsection.
       
  2078                 self.context.append('\\hfill ~}\n')
       
  2079         # TODO: for admonition titles before the first section
       
  2080         # either specify every possible node or ... ?
       
  2081         elif isinstance(node.parent, nodes.sidebar) \
       
  2082         or isinstance(node.parent, nodes.admonition):
       
  2083             self.body.append('\\textbf{\\large ')
       
  2084             self.context.append('}\n\\smallskip\n')
       
  2085         elif isinstance(node.parent, nodes.table):
       
  2086             # caption must be written after column spec
       
  2087             self.active_table.caption = self.encode(node.astext())
       
  2088             raise nodes.SkipNode
       
  2089         elif self.section_level == 0:
       
  2090             # document title
       
  2091             self.title = self.encode(node.astext())
       
  2092             if not self.pdfinfo == None:
       
  2093                 self.pdfinfo.append( 'pdftitle={%s}' % self.encode(node.astext()) )
       
  2094             raise nodes.SkipNode
       
  2095         else:
       
  2096             self.body.append('\n\n')
       
  2097             self.body.append('%' + '_' * 75)
       
  2098             self.body.append('\n\n')
       
  2099             self.bookmark(node)
       
  2100 
       
  2101             if self.use_latex_toc:
       
  2102                 section_star = ""
       
  2103             else:
       
  2104                 section_star = "*"
       
  2105 
       
  2106             section_name = self.d_class.section(self.section_level)
       
  2107             self.body.append('\\%s%s{' % (section_name, section_star))
       
  2108             # MAYBE postfix paragraph and subparagraph with \leavemode to
       
  2109             # ensure floatables stay in the section and text starts on a new line.
       
  2110             self.context.append('}\n')
       
  2111 
       
  2112     def depart_title(self, node):
       
  2113         self.body.append(self.context.pop())
       
  2114         for id in node.parent['ids']:
       
  2115             self.body.append('\\label{%s}\n' % id)
       
  2116 
       
  2117     def visit_topic(self, node):
       
  2118         self.topic_classes = node['classes']
       
  2119         if ('abstract' in self.topic_classes
       
  2120             and self.settings.use_latex_abstract):
       
  2121             self.body.append('\\begin{abstract}\n')
       
  2122 
       
  2123     def depart_topic(self, node):
       
  2124         if ('abstract' in self.topic_classes
       
  2125             and self.settings.use_latex_abstract):
       
  2126             self.body.append('\\end{abstract}\n')
       
  2127         self.topic_classes = []
       
  2128         if 'contents' in node['classes'] and self.use_latex_toc:
       
  2129             pass
       
  2130         else:
       
  2131             self.body.append('\n')
       
  2132 
       
  2133     def visit_inline(self, node): # titlereference
       
  2134         classes = node.get('classes', ['Unknown', ])
       
  2135         for cls in classes:
       
  2136             self.body.append( '\\docutilsrole%s{' % cls)
       
  2137         self.context.append('}'*len(classes))
       
  2138 
       
  2139     def depart_inline(self, node):
       
  2140         self.body.append(self.context.pop())
       
  2141 
       
  2142     def visit_rubric(self, node):
       
  2143         self.body.append('\\rubric{')
       
  2144         self.context.append('}\n')
       
  2145 
       
  2146     def depart_rubric(self, node):
       
  2147         self.body.append(self.context.pop())
       
  2148 
       
  2149     def visit_transition(self, node):
       
  2150         self.body.append('\n\n')
       
  2151         self.body.append('%' + '_' * 75)
       
  2152         self.body.append('\n\\hspace*{\\fill}\\hrulefill\\hspace*{\\fill}')
       
  2153         self.body.append('\n\n')
       
  2154 
       
  2155     def depart_transition(self, node):
       
  2156         pass
       
  2157 
       
  2158     def visit_version(self, node):
       
  2159         self.visit_docinfo_item(node, 'version')
       
  2160 
       
  2161     def depart_version(self, node):
       
  2162         self.depart_docinfo_item(node)
       
  2163 
       
  2164     def visit_warning(self, node):
       
  2165         self.visit_admonition(node, 'warning')
       
  2166 
       
  2167     def depart_warning(self, node):
       
  2168         self.depart_admonition()
       
  2169 
       
  2170     def unimplemented_visit(self, node):
       
  2171         raise NotImplementedError('visiting unimplemented node type: %s'
       
  2172                                   % node.__class__.__name__)
       
  2173 
       
  2174 #    def unknown_visit(self, node):
       
  2175 #    def default_visit(self, node):
       
  2176 
       
  2177 # vim: set ts=4 et ai :