~ubuntu-branches/ubuntu/karmic/python-docutils/karmic

« back to all changes in this revision

Viewing changes to docutils/writers/latex2e/__init__.py

  • Committer: Bazaar Package Importer
  • Author(s): martin f. krafft
  • Date: 2006-07-10 11:45:05 UTC
  • mfrom: (2.1.4 edgy)
  • Revision ID: james.westby@ubuntu.com-20060710114505-otkhqcslevewxmz5
Tags: 0.4-3
Added build dependency on python-central (closes: #377580).

Show diffs side-by-side

added added

removed removed

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