~ubuntu-branches/ubuntu/quantal/python-docutils/quantal

« back to all changes in this revision

Viewing changes to .pc/move-data-to-usr-share.diff/docutils/writers/latex2e/__init__.py

  • Committer: Bazaar Package Importer
  • Author(s): Jakub Wilk
  • Date: 2011-08-07 23:24:56 UTC
  • mfrom: (11.1.2 experimental)
  • Revision ID: james.westby@ubuntu.com-20110807232456-ikquearlmix8hkp1
Tags: 0.7-4
Upload to unstable.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf8 -*-
 
2
# $Id: __init__.py 6348 2010-06-28 22:08:05Z milde $
 
3
# Author: Engelbert Gruber <grubert@users.sourceforge.net>
 
4
# Copyright: This module has been placed in the public domain.
 
5
 
 
6
"""LaTeX2e document tree Writer."""
 
7
 
 
8
__docformat__ = 'reStructuredText'
 
9
 
 
10
# code contributions from several people included, thanks to all.
 
11
# some named: David Abrahams, Julien Letessier, Lele Gaifax, and others.
 
12
#
 
13
# convention deactivate code by two # i.e. ##.
 
14
 
 
15
import sys
 
16
import os
 
17
import time
 
18
import re
 
19
import string
 
20
from docutils import frontend, nodes, languages, writers, utils, io
 
21
from docutils.transforms import writer_aux
 
22
 
 
23
# compatibility module for Python 2.3
 
24
if not hasattr(string, 'Template'):
 
25
    import docutils._string_template_compat
 
26
    string.Template = docutils._string_template_compat.Template
 
27
 
 
28
class Writer(writers.Writer):
 
29
 
 
30
    supported = ('latex','latex2e')
 
31
    """Formats this writer supports."""
 
32
 
 
33
    default_template = 'default.tex'
 
34
    default_template_path = os.path.dirname(__file__)
 
35
 
 
36
    default_preamble = '\n'.join([r'% PDF Standard Fonts',
 
37
                                  r'\usepackage{mathptmx} % Times',
 
38
                                  r'\usepackage[scaled=.90]{helvet}',
 
39
                                  r'\usepackage{courier}'])
 
40
    settings_spec = (
 
41
        'LaTeX-Specific Options',
 
42
        None,
 
43
        (('Specify documentclass.  Default is "article".',
 
44
          ['--documentclass'],
 
45
          {'default': 'article', }),
 
46
         ('Specify document options.  Multiple options can be given, '
 
47
          'separated by commas.  Default is "a4paper".',
 
48
          ['--documentoptions'],
 
49
          {'default': 'a4paper', }),
 
50
         ('Footnotes with numbers/symbols by Docutils. (default)',
 
51
          ['--docutils-footnotes'],
 
52
          {'default': True, 'action': 'store_true',
 
53
           'validator': frontend.validate_boolean}),
 
54
         ('Alias for --docutils-footnotes (deprecated)',
 
55
          ['--use-latex-footnotes'],
 
56
          {'action': 'store_true',
 
57
           'validator': frontend.validate_boolean}),
 
58
         ('Use figure floats for footnote text (deprecated)',
 
59
          ['--figure-footnotes'],
 
60
          {'action': 'store_true',
 
61
           'validator': frontend.validate_boolean}),
 
62
         ('Format for footnote references: one of "superscript" or '
 
63
          '"brackets".  Default is "superscript".',
 
64
          ['--footnote-references'],
 
65
          {'choices': ['superscript', 'brackets'], 'default': 'superscript',
 
66
           'metavar': '<format>',
 
67
           'overrides': 'trim_footnote_reference_space'}),
 
68
         ('Use \\cite command for citations. ',
 
69
          ['--use-latex-citations'],
 
70
          {'default': 0, 'action': 'store_true',
 
71
           'validator': frontend.validate_boolean}),
 
72
         ('Use figure floats for citations '
 
73
          '(might get mixed with real figures). (default)',
 
74
          ['--figure-citations'],
 
75
          {'dest': 'use_latex_citations', 'action': 'store_false',
 
76
           'validator': frontend.validate_boolean}),
 
77
         ('Format for block quote attributions: one of "dash" (em-dash '
 
78
          'prefix), "parentheses"/"parens", or "none".  Default is "dash".',
 
79
          ['--attribution'],
 
80
          {'choices': ['dash', 'parentheses', 'parens', 'none'],
 
81
           'default': 'dash', 'metavar': '<format>'}),
 
82
         ('Specify LaTeX packages/stylesheets. '
 
83
         ' A style is referenced with \\usepackage if extension is '
 
84
         '".sty" or omitted and with \\input else. '
 
85
          ' Overrides previous --stylesheet and --stylesheet-path settings.',
 
86
          ['--stylesheet'],
 
87
          {'default': '', 'metavar': '<file>',
 
88
           'overrides': 'stylesheet_path'}),
 
89
         ('Like --stylesheet, but the path is rewritten '
 
90
          'relative to the output file. ',
 
91
          ['--stylesheet-path'],
 
92
          {'metavar': '<file>', 'overrides': 'stylesheet'}),
 
93
         ('Link to the stylesheet(s) in the output file. (default)',
 
94
          ['--link-stylesheet'],
 
95
          {'dest': 'embed_stylesheet', 'action': 'store_false'}),
 
96
         ('Embed the stylesheet(s) in the output file. '
 
97
          'Stylesheets must be accessible during processing. ',
 
98
          ['--embed-stylesheet'],
 
99
          {'default': 0, 'action': 'store_true',
 
100
           'validator': frontend.validate_boolean}),
 
101
         ('Customization by LaTeX code in the preamble. '
 
102
          'Default: select PDF standard fonts (Times, Helvetica, Courier).',
 
103
          ['--latex-preamble'],
 
104
          {'default': default_preamble}),
 
105
         ('Specify the template file. Default: "%s".' % default_template,
 
106
          ['--template'],
 
107
          {'default': default_template, 'metavar': '<file>'}),
 
108
         ('Table of contents by LaTeX. (default) ',
 
109
          ['--use-latex-toc'],
 
110
          {'default': 1, 'action': 'store_true',
 
111
           'validator': frontend.validate_boolean}),
 
112
         ('Table of contents by Docutils (without page numbers). ',
 
113
          ['--use-docutils-toc'],
 
114
          {'dest': 'use_latex_toc', 'action': 'store_false',
 
115
           'validator': frontend.validate_boolean}),
 
116
         ('Add parts on top of the section hierarchy.',
 
117
          ['--use-part-section'],
 
118
          {'default': 0, 'action': 'store_true',
 
119
           'validator': frontend.validate_boolean}),
 
120
         ('Attach author and date to the document info table. (default) ',
 
121
          ['--use-docutils-docinfo'],
 
122
          {'dest': 'use_latex_docinfo', 'action': 'store_false',
 
123
           'validator': frontend.validate_boolean}),
 
124
         ('Attach author and date to the document title.',
 
125
          ['--use-latex-docinfo'],
 
126
          {'default': 0, 'action': 'store_true',
 
127
           'validator': frontend.validate_boolean}),
 
128
         ("Typeset abstract as topic. (default)",
 
129
          ['--topic-abstract'],
 
130
          {'dest': 'use_latex_abstract', 'action': 'store_false',
 
131
           'validator': frontend.validate_boolean}),
 
132
         ("Use LaTeX abstract environment for the document's abstract. ",
 
133
          ['--use-latex-abstract'],
 
134
          {'default': 0, 'action': 'store_true',
 
135
           'validator': frontend.validate_boolean}),
 
136
         ('Color of any hyperlinks embedded in text '
 
137
          '(default: "blue", "0" to disable).',
 
138
          ['--hyperlink-color'], {'default': 'blue'}),
 
139
         ('Enable compound enumerators for nested enumerated lists '
 
140
          '(e.g. "1.2.a.ii").  Default: disabled.',
 
141
          ['--compound-enumerators'],
 
142
          {'default': None, 'action': 'store_true',
 
143
           'validator': frontend.validate_boolean}),
 
144
         ('Disable compound enumerators for nested enumerated lists. '
 
145
          'This is the default.',
 
146
          ['--no-compound-enumerators'],
 
147
          {'action': 'store_false', 'dest': 'compound_enumerators'}),
 
148
         ('Enable section ("." subsection ...) prefixes for compound '
 
149
          'enumerators.  This has no effect without --compound-enumerators.'
 
150
          'Default: disabled.',
 
151
          ['--section-prefix-for-enumerators'],
 
152
          {'default': None, 'action': 'store_true',
 
153
           'validator': frontend.validate_boolean}),
 
154
         ('Disable section prefixes for compound enumerators.  '
 
155
          'This is the default.',
 
156
          ['--no-section-prefix-for-enumerators'],
 
157
          {'action': 'store_false', 'dest': 'section_prefix_for_enumerators'}),
 
158
         ('Set the separator between section number and enumerator '
 
159
          'for compound enumerated lists.  Default is "-".',
 
160
          ['--section-enumerator-separator'],
 
161
          {'default': '-', 'metavar': '<char>'}),
 
162
         ('When possibile, use the specified environment for literal-blocks. '
 
163
          'Default is quoting of whitespace and special chars.',
 
164
          ['--literal-block-env'],
 
165
          {'default': ''}),
 
166
         ('When possibile, use verbatim for literal-blocks. '
 
167
          'Compatibility alias for "--literal-block-env=verbatim".',
 
168
          ['--use-verbatim-when-possible'],
 
169
          {'default': 0, 'action': 'store_true',
 
170
           'validator': frontend.validate_boolean}),
 
171
         ('Table style. "standard" with horizontal and vertical lines, '
 
172
          '"booktabs" (LaTeX booktabs style) only horizontal lines '
 
173
          'above and below the table and below the header or "borderless".  '
 
174
          'Default: "standard"',
 
175
          ['--table-style'],
 
176
          {'choices': ['standard', 'booktabs','nolines', 'borderless'],
 
177
           'default': 'standard',
 
178
           'metavar': '<format>'}),
 
179
         ('LaTeX graphicx package option. '
 
180
          'Possible values are "dvips", "pdftex". "auto" includes LaTeX code '
 
181
          'to use "pdftex" if processing with pdf(la)tex and dvips otherwise. '
 
182
          'Default is no option.',
 
183
          ['--graphicx-option'],
 
184
          {'default': ''}),
 
185
         ('LaTeX font encoding. '
 
186
          'Possible values are "", "T1" (default), "OT1", "LGR,T1" or '
 
187
          'any other combination of options to the `fontenc` package. ',
 
188
          ['--font-encoding'],
 
189
          {'default': 'T1'}),
 
190
         ('Per default the latex-writer puts the reference title into '
 
191
          'hyperreferences. Specify "ref*" or "pageref*" to get the section '
 
192
          'number or the page number.',
 
193
          ['--reference-label'],
 
194
          {'default': None, }),
 
195
         ('Specify style and database for bibtex, for example '
 
196
          '"--use-bibtex=mystyle,mydb1,mydb2".',
 
197
          ['--use-bibtex'],
 
198
          {'default': None, }),
 
199
          ),)
 
200
 
 
201
    settings_defaults = {'sectnum_depth': 0 # updated by SectNum transform
 
202
                        }
 
203
    relative_path_settings = ('stylesheet_path',)
 
204
 
 
205
    config_section = 'latex2e writer'
 
206
    config_section_dependencies = ('writers',)
 
207
 
 
208
    head_parts = ('head_prefix', 'requirements', 'latex_preamble',
 
209
                  'stylesheet', 'fallbacks', 'pdfsetup', 'title', 'subtitle')
 
210
    visitor_attributes = head_parts + ('body_pre_docinfo', 'docinfo',
 
211
                                       'dedication', 'abstract', 'body')
 
212
 
 
213
    output = None
 
214
    """Final translated form of `document`."""
 
215
 
 
216
    def __init__(self):
 
217
        writers.Writer.__init__(self)
 
218
        self.translator_class = LaTeXTranslator
 
219
 
 
220
    # Override parent method to add latex-specific transforms
 
221
    def get_transforms(self):
 
222
       # call the parent class' method
 
223
       transform_list = writers.Writer.get_transforms(self)
 
224
       # print transform_list
 
225
       # Convert specific admonitions to generic one
 
226
       transform_list.append(writer_aux.Admonitions)
 
227
       # TODO: footnote collection transform
 
228
       # transform_list.append(footnotes.collect)
 
229
       return transform_list
 
230
 
 
231
 
 
232
    def translate(self):
 
233
        visitor = self.translator_class(self.document)
 
234
        self.document.walkabout(visitor)
 
235
        # copy parts
 
236
        for part in self.visitor_attributes:
 
237
            setattr(self, part, getattr(visitor, part))
 
238
        # get template string from file
 
239
        try:
 
240
            file = open(self.document.settings.template, 'rb')
 
241
        except IOError:
 
242
            file = open(os.path.join(os.path.dirname(__file__),
 
243
                                     self.document.settings.template), 'rb')
 
244
        template = string.Template(unicode(file.read(), 'utf-8'))
 
245
        file.close()
 
246
        # fill template
 
247
        self.assemble_parts() # create dictionary of parts
 
248
        self.output = template.substitute(self.parts)
 
249
 
 
250
    def assemble_parts(self):
 
251
        """Assemble the `self.parts` dictionary of output fragments."""
 
252
        writers.Writer.assemble_parts(self)
 
253
        for part in self.visitor_attributes:
 
254
            lines = getattr(self, part)
 
255
            if part in self.head_parts:
 
256
                if lines:
 
257
                    lines.append('') # to get a trailing newline
 
258
                self.parts[part] = '\n'.join(lines)
 
259
            else:
 
260
                # body contains inline elements, so join without newline
 
261
                self.parts[part] = ''.join(lines)
 
262
 
 
263
 
 
264
class Babel(object):
 
265
    """Language specifics for LaTeX."""
 
266
    # country code by a.schlock.
 
267
    # partly manually converted from iso and babel stuff, dialects and some
 
268
    _ISO639_TO_BABEL = {
 
269
        'no': 'norsk',     #XXX added by hand ( forget about nynorsk?)
 
270
        'gd': 'scottish',  #XXX added by hand
 
271
        'hu': 'magyar',    #XXX added by hand
 
272
        'pt': 'portuguese',#XXX added by hand
 
273
        'sl': 'slovenian',
 
274
        'af': 'afrikaans',
 
275
        'bg': 'bulgarian',
 
276
        'br': 'breton',
 
277
        'ca': 'catalan',
 
278
        'cs': 'czech',
 
279
        'cy': 'welsh',
 
280
        'da': 'danish',
 
281
        'fr': 'french',
 
282
        # french, francais, canadien, acadian
 
283
        'de': 'ngerman',  #XXX rather than german
 
284
        # ngerman, naustrian, german, germanb, austrian
 
285
        'el': 'greek',
 
286
        'en': 'english',
 
287
        # english, USenglish, american, UKenglish, british, canadian
 
288
        'eo': 'esperanto',
 
289
        'es': 'spanish',
 
290
        'et': 'estonian',
 
291
        'eu': 'basque',
 
292
        'fi': 'finnish',
 
293
        'ga': 'irish',
 
294
        'gl': 'galician',
 
295
        'he': 'hebrew',
 
296
        'hr': 'croatian',
 
297
        'hu': 'hungarian',
 
298
        'is': 'icelandic',
 
299
        'it': 'italian',
 
300
        'la': 'latin',
 
301
        'nl': 'dutch',
 
302
        'pl': 'polish',
 
303
        'pt': 'portuguese',
 
304
        'ro': 'romanian',
 
305
        'ru': 'russian',
 
306
        'sk': 'slovak',
 
307
        'sr': 'serbian',
 
308
        'sv': 'swedish',
 
309
        'tr': 'turkish',
 
310
        'uk': 'ukrainian'
 
311
    }
 
312
 
 
313
    def __init__(self, lang):
 
314
        self.language = lang
 
315
        self.quote_index = 0
 
316
        self.quotes = ('``', "''")
 
317
        self.setup = '' # language dependent configuration code
 
318
        # double quotes are "active" in some languages (e.g. German).
 
319
        # TODO: use \textquotedbl in OT1 font encoding?
 
320
        self.literal_double_quote = u'"'
 
321
        if self.language.startswith('de'):
 
322
            self.quotes = (r'\glqq{}', r'\grqq{}')
 
323
            self.literal_double_quote = ur'\dq{}'
 
324
        if self.language.startswith('it'):
 
325
            self.literal_double_quote = ur'{\char`\"}'
 
326
        if self.language.startswith('es'):
 
327
            # reset tilde ~ to the original binding (nobreakspace):
 
328
            self.setup = ('\n'
 
329
                  r'\addto\shorthandsspanish{\spanishdeactivate{."~<>}}')
 
330
 
 
331
    def next_quote(self):
 
332
        q = self.quotes[self.quote_index]
 
333
        self.quote_index = (self.quote_index+1) % 2
 
334
        return q
 
335
 
 
336
    def quote_quotes(self,text):
 
337
        t = None
 
338
        for part in text.split('"'):
 
339
            if t == None:
 
340
                t = part
 
341
            else:
 
342
                t += self.next_quote() + part
 
343
        return t
 
344
 
 
345
    def get_language(self):
 
346
        lang = self.language.split('_')[0]  # filter dialects
 
347
        return self._ISO639_TO_BABEL.get(lang, "")
 
348
 
 
349
# Building blocks for the latex preamble
 
350
# --------------------------------------
 
351
 
 
352
class SortableDict(dict):
 
353
    """Dictionary with additional sorting methods
 
354
 
 
355
    Tip: use key starting with with '_' for sorting before small letters
 
356
         and with '~' for sorting after small letters.
 
357
    """
 
358
    def sortedkeys(self):
 
359
        """Return sorted list of keys"""
 
360
        keys = self.keys()
 
361
        keys.sort()
 
362
        return keys
 
363
 
 
364
    def sortedvalues(self):
 
365
        """Return list of values sorted by keys"""
 
366
        return [self[key] for key in self.sortedkeys()]
 
367
 
 
368
 
 
369
# PreambleCmds
 
370
# `````````````
 
371
# A container for LaTeX code snippets that can be
 
372
# inserted into the preamble if required in the document.
 
373
#
 
374
# .. The package 'makecmds' would enable shorter definitions using the
 
375
#    \providelength and \provideenvironment commands.
 
376
#    However, it is pretty non-standard (texlive-latex-extra).
 
377
 
 
378
class PreambleCmds(object):
 
379
    """Building blocks for the latex preamble."""
 
380
 
 
381
PreambleCmds.abstract = r"""
 
382
% abstract title
 
383
\providecommand*{\DUtitleabstract}[1]{\centerline{\textbf{#1}}}"""
 
384
 
 
385
PreambleCmds.admonition = r"""
 
386
% admonition (specially marked topic)
 
387
\providecommand{\DUadmonition}[2][class-arg]{%
 
388
  % try \DUadmonition#1{#2}:
 
389
  \ifcsname DUadmonition#1\endcsname%
 
390
    \csname DUadmonition#1\endcsname{#2}%
 
391
  \else
 
392
    \begin{center}
 
393
      \fbox{\parbox{0.9\textwidth}{#2}}
 
394
    \end{center}
 
395
  \fi
 
396
}"""
 
397
 
 
398
## PreambleCmds.caption = r"""% configure caption layout
 
399
## \usepackage{caption}
 
400
## \captionsetup{singlelinecheck=false}% no exceptions for one-liners"""
 
401
 
 
402
PreambleCmds.color = r"""\usepackage{color}"""
 
403
 
 
404
PreambleCmds.docinfo = r"""
 
405
% docinfo (width of docinfo table)
 
406
\DUprovidelength{\DUdocinfowidth}{0.9\textwidth}"""
 
407
# PreambleCmds.docinfo._depends = 'providelength'
 
408
 
 
409
PreambleCmds.embedded_package_wrapper = r"""\makeatletter
 
410
%% embedded stylesheet: %s
 
411
%s
 
412
\makeatother"""
 
413
 
 
414
PreambleCmds.dedication = r"""
 
415
% dedication topic
 
416
\providecommand{\DUtopicdedication}[1]{\begin{center}#1\end{center}}"""
 
417
 
 
418
PreambleCmds.error = r"""
 
419
% error admonition title
 
420
\providecommand*{\DUtitleerror}[1]{\DUtitle{\color{red}#1}}"""
 
421
# PreambleCmds.errortitle._depends = 'color'
 
422
 
 
423
PreambleCmds.fieldlist = r"""
 
424
% fieldlist environment
 
425
\ifthenelse{\isundefined{\DUfieldlist}}{
 
426
  \newenvironment{DUfieldlist}%
 
427
    {\quote\description}
 
428
    {\enddescription\endquote}
 
429
}{}"""
 
430
 
 
431
PreambleCmds.float_settings = r"""\usepackage{float} % float configuration
 
432
\floatplacement{figure}{H} % place figures here definitely"""
 
433
 
 
434
PreambleCmds.footnotes = r"""% numeric or symbol footnotes with hyperlinks
 
435
\providecommand*{\DUfootnotemark}[3]{%
 
436
  \raisebox{1em}{\hypertarget{#1}{}}%
 
437
  \hyperlink{#2}{\textsuperscript{#3}}%
 
438
}
 
439
\providecommand{\DUfootnotetext}[4]{%
 
440
  \begingroup%
 
441
  \renewcommand{\thefootnote}{%
 
442
    \protect\raisebox{1em}{\protect\hypertarget{#1}{}}%
 
443
    \protect\hyperlink{#2}{#3}}%
 
444
  \footnotetext{#4}%
 
445
  \endgroup%
 
446
}"""
 
447
 
 
448
PreambleCmds.footnote_floats = r"""% settings for footnotes as floats:
 
449
\setlength{\floatsep}{0.5em}
 
450
\setlength{\textfloatsep}{\fill}
 
451
\addtolength{\textfloatsep}{3em}
 
452
\renewcommand{\textfraction}{0.5}
 
453
\renewcommand{\topfraction}{0.5}
 
454
\renewcommand{\bottomfraction}{0.5}
 
455
\setcounter{totalnumber}{50}
 
456
\setcounter{topnumber}{50}
 
457
\setcounter{bottomnumber}{50}"""
 
458
 
 
459
PreambleCmds.graphicx_auto = r"""% Check output format
 
460
\ifx\pdftexversion\undefined
 
461
  \usepackage{graphicx}
 
462
\else
 
463
  \usepackage[pdftex]{graphicx}
 
464
\fi'))"""
 
465
 
 
466
 
 
467
PreambleCmds.inline = r"""
 
468
% inline markup (custom roles)
 
469
% \DUrole{#1}{#2} tries \DUrole#1{#2}
 
470
\providecommand*{\DUrole}[2]{%
 
471
  \ifcsname DUrole#1\endcsname%
 
472
    \csname DUrole#1\endcsname{#2}%
 
473
  \else% backwards compatibility: try \docutilsrole#1{#2}
 
474
    \ifcsname docutilsrole#1\endcsname%
 
475
      \csname docutilsrole#1\endcsname{#2}%
 
476
    \else%
 
477
      #2%
 
478
    \fi%
 
479
  \fi%
 
480
}"""
 
481
 
 
482
PreambleCmds.legend = r"""
 
483
% legend environment
 
484
\ifthenelse{\isundefined{\DUlegend}}{
 
485
  \newenvironment{DUlegend}{\small}{}
 
486
}{}"""
 
487
 
 
488
PreambleCmds.lineblock = r"""
 
489
% lineblock environment
 
490
\DUprovidelength{\DUlineblockindent}{2.5em}
 
491
\ifthenelse{\isundefined{\DUlineblock}}{
 
492
  \newenvironment{DUlineblock}[1]{%
 
493
    \list{}{\setlength{\partopsep}{\parskip}
 
494
            \addtolength{\partopsep}{\baselineskip}
 
495
            \setlength{\topsep}{0pt}
 
496
            \setlength{\itemsep}{0.15\baselineskip}
 
497
            \setlength{\parsep}{0pt}
 
498
            \setlength{\leftmargin}{#1}}
 
499
    \raggedright
 
500
  }
 
501
  {\endlist}
 
502
}{}"""
 
503
# PreambleCmds.lineblock._depends = 'providelength'
 
504
 
 
505
PreambleCmds.linking = r"""
 
506
%% hyperlinks:
 
507
\ifthenelse{\isundefined{\hypersetup}}{
 
508
  \usepackage[unicode,colorlinks=%s,linkcolor=%s,urlcolor=%s]{hyperref}
 
509
  \urlstyle{same} %% normal text font (alternatives: tt, rm, sf)
 
510
}{}"""
 
511
 
 
512
PreambleCmds.minitoc = r"""%% local table of contents
 
513
\usepackage{minitoc}"""
 
514
 
 
515
PreambleCmds.optionlist = r"""
 
516
% optionlist environment
 
517
\providecommand*{\DUoptionlistlabel}[1]{\bf #1 \hfill}
 
518
\DUprovidelength{\DUoptionlistindent}{3cm}
 
519
\ifthenelse{\isundefined{\DUoptionlist}}{
 
520
  \newenvironment{DUoptionlist}{%
 
521
    \list{}{\setlength{\labelwidth}{\DUoptionlistindent}
 
522
            \setlength{\rightmargin}{1cm}
 
523
            \setlength{\leftmargin}{\rightmargin}
 
524
            \addtolength{\leftmargin}{\labelwidth}
 
525
            \addtolength{\leftmargin}{\labelsep}
 
526
            \renewcommand{\makelabel}{\DUoptionlistlabel}}
 
527
  }
 
528
  {\endlist}
 
529
}{}"""
 
530
# PreambleCmds.optionlist._depends = 'providelength'
 
531
 
 
532
PreambleCmds.providelength = r"""
 
533
% providelength (provide a length variable and set default, if it is new)
 
534
\providecommand*{\DUprovidelength}[2]{
 
535
  \ifthenelse{\isundefined{#1}}{\newlength{#1}\setlength{#1}{#2}}{}
 
536
}"""
 
537
 
 
538
PreambleCmds.rubric = r"""
 
539
% rubric (informal heading)
 
540
\providecommand*{\DUrubric}[2][class-arg]{%
 
541
  \subsubsection*{\centering\textit{\textmd{#2}}}}"""
 
542
 
 
543
PreambleCmds.sidebar = r"""
 
544
% sidebar (text outside the main text flow)
 
545
\providecommand{\DUsidebar}[2][class-arg]{%
 
546
  \begin{center}
 
547
    \colorbox[gray]{0.80}{\parbox{0.9\textwidth}{#2}}
 
548
  \end{center}
 
549
}"""
 
550
 
 
551
PreambleCmds.subtitle = r"""
 
552
% subtitle (for topic/sidebar)
 
553
\providecommand*{\DUsubtitle}[2][class-arg]{\par\emph{#2}\smallskip}"""
 
554
 
 
555
PreambleCmds.table = r"""\usepackage{longtable}
 
556
\usepackage{array}
 
557
\setlength{\extrarowheight}{2pt}
 
558
\newlength{\DUtablewidth} % internal use in tables"""
 
559
 
 
560
# Options [force,almostfull] prevent spurious error messages, see
 
561
# de.comp.text.tex/2005-12/msg01855
 
562
PreambleCmds.textcomp = """\
 
563
\\usepackage{textcomp} % text symbol macros"""
 
564
 
 
565
PreambleCmds.documenttitle = r"""
 
566
%% Document title
 
567
\title{%s}
 
568
\author{%s}
 
569
\date{%s}
 
570
\maketitle
 
571
"""
 
572
 
 
573
PreambleCmds.titlereference = r"""
 
574
% titlereference role
 
575
\providecommand*{\DUroletitlereference}[1]{\textsl{#1}}"""
 
576
 
 
577
PreambleCmds.title = r"""
 
578
% title for topics, admonitions and sidebar
 
579
\providecommand*{\DUtitle}[2][class-arg]{%
 
580
  % call \DUtitle#1{#2} if it exists:
 
581
  \ifcsname DUtitle#1\endcsname%
 
582
    \csname DUtitle#1\endcsname{#2}%
 
583
  \else
 
584
    \smallskip\noindent\textbf{#2}\smallskip%
 
585
  \fi
 
586
}"""
 
587
 
 
588
PreambleCmds.topic = r"""
 
589
% topic (quote with heading)
 
590
\providecommand{\DUtopic}[2][class-arg]{%
 
591
  \ifcsname DUtopic#1\endcsname%
 
592
    \csname DUtopic#1\endcsname{#2}%
 
593
  \else
 
594
    \begin{quote}#2\end{quote}
 
595
  \fi
 
596
}"""
 
597
 
 
598
PreambleCmds.transition = r"""
 
599
% transition (break, fancybreak, anonymous section)
 
600
\providecommand*{\DUtransition}[1][class-arg]{%
 
601
  \hspace*{\fill}\hrulefill\hspace*{\fill}
 
602
  \vskip 0.5\baselineskip
 
603
}"""
 
604
 
 
605
 
 
606
class DocumentClass(object):
 
607
    """Details of a LaTeX document class."""
 
608
 
 
609
    def __init__(self, document_class, with_part=False):
 
610
        self.document_class = document_class
 
611
        self._with_part = with_part
 
612
        self.sections = ['section', 'subsection', 'subsubsection',
 
613
                         'paragraph', 'subparagraph']
 
614
        if self.document_class in ('book', 'memoir', 'report',
 
615
                                   'scrbook', 'scrreprt'):
 
616
            self.sections.insert(0, 'chapter')
 
617
        if self._with_part:
 
618
            self.sections.insert(0, 'part')
 
619
 
 
620
    def section(self, level):
 
621
        """Return the LaTeX section name for section `level`.
 
622
 
 
623
        The name depends on the specific document class.
 
624
        Level is 1,2,3..., as level 0 is the title.
 
625
        """
 
626
 
 
627
        if level <= len(self.sections):
 
628
            return self.sections[level-1]
 
629
        else:
 
630
            return self.sections[-1]
 
631
 
 
632
 
 
633
class Table(object):
 
634
    """Manage a table while traversing.
 
635
 
 
636
    Maybe change to a mixin defining the visit/departs, but then
 
637
    class Table internal variables are in the Translator.
 
638
 
 
639
    Table style might be
 
640
 
 
641
    :standard:   horizontal and vertical lines
 
642
    :booktabs:   only horizontal lines (requires "booktabs" LaTeX package)
 
643
    :borderless: no borders around table cells
 
644
    :nolines:    alias for borderless
 
645
    """
 
646
    def __init__(self,translator,latex_type,table_style):
 
647
        self._translator = translator
 
648
        self._latex_type = latex_type
 
649
        self._table_style = table_style
 
650
        self._open = 0
 
651
        # miscellaneous attributes
 
652
        self._attrs = {}
 
653
        self._col_width = []
 
654
        self._rowspan = []
 
655
        self.stubs = []
 
656
        self._in_thead = 0
 
657
 
 
658
    def open(self):
 
659
        self._open = 1
 
660
        self._col_specs = []
 
661
        self.caption = []
 
662
        self._attrs = {}
 
663
        self._in_head = 0 # maybe context with search
 
664
    def close(self):
 
665
        self._open = 0
 
666
        self._col_specs = None
 
667
        self.caption = []
 
668
        self._attrs = {}
 
669
        self.stubs = []
 
670
    def is_open(self):
 
671
        return self._open
 
672
 
 
673
    def set_table_style(self, table_style):
 
674
        if not table_style in ('standard','booktabs','borderless','nolines'):
 
675
            return
 
676
        self._table_style = table_style
 
677
 
 
678
    def get_latex_type(self):
 
679
        return self._latex_type
 
680
 
 
681
    def set(self,attr,value):
 
682
        self._attrs[attr] = value
 
683
    def get(self,attr):
 
684
        if attr in self._attrs:
 
685
            return self._attrs[attr]
 
686
        return None
 
687
 
 
688
    def get_vertical_bar(self):
 
689
        if self._table_style == 'standard':
 
690
            return '|'
 
691
        return ''
 
692
 
 
693
    # horizontal lines are drawn below a row,
 
694
    def get_opening(self):
 
695
        if self._latex_type == 'longtable':
 
696
            # otherwise longtable might move before paragraph and subparagraph
 
697
            prefix = '\\leavevmode\n'
 
698
        else:
 
699
            prefix = ''
 
700
        prefix += '\setlength{\DUtablewidth}{\linewidth}'
 
701
        return '%s\n\\begin{%s}[c]' % (prefix, self._latex_type)
 
702
 
 
703
    def get_closing(self):
 
704
        line = ''
 
705
        if self._table_style == 'booktabs':
 
706
            line = '\\bottomrule\n'
 
707
        elif self._table_style == 'standard':
 
708
            lines = '\\hline\n'
 
709
        return '%s\\end{%s}' % (line,self._latex_type)
 
710
 
 
711
    def visit_colspec(self, node):
 
712
        self._col_specs.append(node)
 
713
        # "stubs" list is an attribute of the tgroup element:
 
714
        self.stubs.append(node.attributes.get('stub'))
 
715
 
 
716
    def get_colspecs(self):
 
717
        """Return column specification for longtable.
 
718
 
 
719
        Assumes reST line length being 80 characters.
 
720
        Table width is hairy.
 
721
 
 
722
        === ===
 
723
        ABC DEF
 
724
        === ===
 
725
 
 
726
        usually gets to narrow, therefore we add 1 (fiddlefactor).
 
727
        """
 
728
        width = 80
 
729
 
 
730
        total_width = 0.0
 
731
        # first see if we get too wide.
 
732
        for node in self._col_specs:
 
733
            colwidth = float(node['colwidth']+1) / width
 
734
            total_width += colwidth
 
735
        self._col_width = []
 
736
        self._rowspan = []
 
737
        # donot make it full linewidth
 
738
        factor = 0.93
 
739
        if total_width > 1.0:
 
740
            factor /= total_width
 
741
        bar = self.get_vertical_bar()
 
742
        latex_table_spec = ''
 
743
        for node in self._col_specs:
 
744
            colwidth = factor * float(node['colwidth']+1) / width
 
745
            self._col_width.append(colwidth+0.005)
 
746
            self._rowspan.append(0)
 
747
            latex_table_spec += '%sp{%.3f\\DUtablewidth}' % (bar, colwidth+0.005)
 
748
        return latex_table_spec+bar
 
749
 
 
750
    def get_column_width(self):
 
751
        """Return columnwidth for current cell (not multicell)."""
 
752
        return '%.2f\\DUtablewidth' % self._col_width[self._cell_in_row-1]
 
753
 
 
754
    def get_caption(self):
 
755
        if not self.caption:
 
756
            return ''
 
757
        caption = ''.join(self.caption)
 
758
        if 1 == self._translator.thead_depth():
 
759
            return r'\caption{%s}\\' '\n' % caption
 
760
        return r'\caption[]{%s (... continued)}\\' '\n' % caption
 
761
 
 
762
    def need_recurse(self):
 
763
        if self._latex_type == 'longtable':
 
764
            return 1 == self._translator.thead_depth()
 
765
        return 0
 
766
 
 
767
    def visit_thead(self):
 
768
        self._in_thead += 1
 
769
        if self._table_style == 'standard':
 
770
            return ['\\hline\n']
 
771
        elif self._table_style == 'booktabs':
 
772
            return ['\\toprule\n']
 
773
        return []
 
774
    def depart_thead(self):
 
775
        a = []
 
776
        #if self._table_style == 'standard':
 
777
        #    a.append('\\hline\n')
 
778
        if self._table_style == 'booktabs':
 
779
            a.append('\\midrule\n')
 
780
        if self._latex_type == 'longtable':
 
781
            if 1 == self._translator.thead_depth():
 
782
                a.append('\\endfirsthead\n')
 
783
            else:
 
784
                a.append('\\endhead\n')
 
785
                a.append(r'\multicolumn{%d}{c}' % len(self._col_specs) +
 
786
                         r'{\hfill ... continued on next page} \\')
 
787
                a.append('\n\\endfoot\n\\endlastfoot\n')
 
788
        # for longtable one could add firsthead, foot and lastfoot
 
789
        self._in_thead -= 1
 
790
        return a
 
791
    def visit_row(self):
 
792
        self._cell_in_row = 0
 
793
    def depart_row(self):
 
794
        res = [' \\\\\n']
 
795
        self._cell_in_row = None  # remove cell counter
 
796
        for i in range(len(self._rowspan)):
 
797
            if (self._rowspan[i]>0):
 
798
                self._rowspan[i] -= 1
 
799
 
 
800
        if self._table_style == 'standard':
 
801
            rowspans = [i+1 for i in range(len(self._rowspan))
 
802
                        if (self._rowspan[i]<=0)]
 
803
            if len(rowspans)==len(self._rowspan):
 
804
                res.append('\\hline\n')
 
805
            else:
 
806
                cline = ''
 
807
                rowspans.reverse()
 
808
                # TODO merge clines
 
809
                while 1:
 
810
                    try:
 
811
                        c_start = rowspans.pop()
 
812
                    except:
 
813
                        break
 
814
                    cline += '\\cline{%d-%d}\n' % (c_start,c_start)
 
815
                res.append(cline)
 
816
        return res
 
817
 
 
818
    def set_rowspan(self,cell,value):
 
819
        try:
 
820
            self._rowspan[cell] = value
 
821
        except:
 
822
            pass
 
823
    def get_rowspan(self,cell):
 
824
        try:
 
825
            return self._rowspan[cell]
 
826
        except:
 
827
            return 0
 
828
    def get_entry_number(self):
 
829
        return self._cell_in_row
 
830
    def visit_entry(self):
 
831
        self._cell_in_row += 1
 
832
    def is_stub_column(self):
 
833
        if len(self.stubs) >= self._cell_in_row:
 
834
            return self.stubs[self._cell_in_row-1]
 
835
        return False
 
836
 
 
837
 
 
838
class LaTeXTranslator(nodes.NodeVisitor):
 
839
 
 
840
    # When options are given to the documentclass, latex will pass them
 
841
    # to other packages, as done with babel.
 
842
    # Dummy settings might be taken from document settings
 
843
 
 
844
    # Config setting defaults
 
845
    # -----------------------
 
846
 
 
847
    # TODO: use mixins for different implementations.
 
848
    # list environment for docinfo. else tabularx
 
849
    ## use_optionlist_for_docinfo = False # TODO: NOT YET IN USE
 
850
 
 
851
    # Use compound enumerations (1.A.1.)
 
852
    compound_enumerators = 0
 
853
 
 
854
    # If using compound enumerations, include section information.
 
855
    section_prefix_for_enumerators = 0
 
856
 
 
857
    # This is the character that separates the section ("." subsection ...)
 
858
    # prefix from the regular list enumerator.
 
859
    section_enumerator_separator = '-'
 
860
 
 
861
    # default link color
 
862
    hyperlink_color = 'blue'
 
863
 
 
864
    # Auxiliary variables
 
865
    # -------------------
 
866
 
 
867
    has_latex_toc = False # is there a toc in the doc? (needed by minitoc)
 
868
    is_toc_list = False   # is the current bullet_list a ToC?
 
869
    section_level = 0
 
870
 
 
871
    # Flags to encode():
 
872
    # inside citation reference labels underscores dont need to be escaped
 
873
    inside_citation_reference_label = False
 
874
    verbatim = False                   # do not encode
 
875
    insert_non_breaking_blanks = False # replace blanks by "~"
 
876
    insert_newline = False             # add latex newline commands
 
877
    literal = False                    # literal text (block or inline)
 
878
 
 
879
 
 
880
    def __init__(self, document):
 
881
        nodes.NodeVisitor.__init__(self, document)
 
882
        # Reporter
 
883
        # ~~~~~~~~
 
884
        self.warn = self.document.reporter.warning
 
885
        self.error = self.document.reporter.error
 
886
 
 
887
        # Settings
 
888
        # ~~~~~~~~
 
889
        self.settings = settings = document.settings
 
890
        self.latex_encoding = self.to_latex_encoding(settings.output_encoding)
 
891
        self.use_latex_toc = settings.use_latex_toc
 
892
        self.use_latex_docinfo = settings.use_latex_docinfo
 
893
        self._use_latex_citations = settings.use_latex_citations
 
894
        self.embed_stylesheet = settings.embed_stylesheet
 
895
        self._reference_label = settings.reference_label
 
896
        self.hyperlink_color = settings.hyperlink_color
 
897
        self.compound_enumerators = settings.compound_enumerators
 
898
        self.font_encoding = settings.font_encoding
 
899
        self.section_prefix_for_enumerators = (
 
900
            settings.section_prefix_for_enumerators)
 
901
        self.section_enumerator_separator = (
 
902
            settings.section_enumerator_separator.replace('_', '\\_'))
 
903
        # literal blocks:
 
904
        self.literal_block_env = ''
 
905
        self.literal_block_options = ''
 
906
        if settings.literal_block_env != '':
 
907
            (none,
 
908
             self.literal_block_env,
 
909
             self.literal_block_options,
 
910
             none ) = re.split('(\w+)(.*)', settings.literal_block_env)
 
911
        elif settings.use_verbatim_when_possible:
 
912
            self.literal_block_env = 'verbatim'
 
913
        #
 
914
        if self.settings.use_bibtex:
 
915
            self.bibtex = self.settings.use_bibtex.split(',',1)
 
916
            # TODO avoid errors on not declared citations.
 
917
        else:
 
918
            self.bibtex = None
 
919
        # language:
 
920
        # (labels, bibliographic_fields, and author_separators)
 
921
        self.language = languages.get_language(settings.language_code)
 
922
        self.babel = Babel(settings.language_code)
 
923
        self.author_separator = self.language.author_separators[0]
 
924
        self.d_options = [self.settings.documentoptions,
 
925
                          self.babel.get_language()]
 
926
        self.d_options = ','.join([opt for opt in self.d_options if opt])
 
927
        self.d_class = DocumentClass(settings.documentclass,
 
928
                                     settings.use_part_section)
 
929
        # graphic package options:
 
930
        if self.settings.graphicx_option == '':
 
931
            self.graphicx_package = r'\usepackage{graphicx}'
 
932
        elif self.settings.graphicx_option.lower() == 'auto':
 
933
            self.graphicx_package = PreambleCmds.graphicx_auto
 
934
        else:
 
935
            self.graphicx_package = (r'\usepackage[%s]{graphicx}' %
 
936
                                     self.settings.graphicx_option)
 
937
        # footnotes:
 
938
        self.docutils_footnotes = settings.docutils_footnotes
 
939
        if settings.use_latex_footnotes:
 
940
            self.docutils_footnotes = True
 
941
            self.warn('`use_latex_footnotes` is deprecated. '
 
942
                      'The setting has been renamed to `docutils_footnotes` '
 
943
                      'and the alias will be removed in a future version.')
 
944
        self.figure_footnotes = settings.figure_footnotes
 
945
        if self.figure_footnotes:
 
946
            self.docutils_footnotes = True
 
947
            self.warn('The "figure footnotes" workaround/setting is strongly '
 
948
                      'deprecated and will be removed in a future version.')
 
949
 
 
950
        # Output collection stacks
 
951
        # ~~~~~~~~~~~~~~~~~~~~~~~~
 
952
 
 
953
        # Document parts
 
954
        self.head_prefix = [r'\documentclass[%s]{%s}' %
 
955
                            (self.d_options, self.settings.documentclass)]
 
956
        self.requirements = SortableDict() # made a list in depart_document()
 
957
        self.latex_preamble = [settings.latex_preamble]
 
958
        self.stylesheet = []
 
959
        self.fallbacks = SortableDict() # made a list in depart_document()
 
960
        self.pdfsetup = [] # PDF properties (hyperref package)
 
961
        self.title = []
 
962
        self.subtitle = []
 
963
        ## self.body_prefix = ['\\begin{document}\n']
 
964
        self.body_pre_docinfo = [] # title data and \maketitle
 
965
        self.docinfo = []
 
966
        self.dedication = []
 
967
        self.abstract = []
 
968
        self.body = []
 
969
        ## self.body_suffix = ['\\end{document}\n']
 
970
 
 
971
        # A heterogenous stack used in conjunction with the tree traversal.
 
972
        # Make sure that the pops correspond to the pushes:
 
973
        self.context = []
 
974
 
 
975
        # Title metadata:
 
976
        self.title_labels = []
 
977
        self.subtitle_labels = []
 
978
        # (if use_latex_docinfo: collects lists of
 
979
        # author/organization/contact/address lines)
 
980
        self.author_stack = []
 
981
        # date (the default supresses the "auto-date" feature of \maketitle)
 
982
        self.date = []
 
983
 
 
984
        # PDF properties: pdftitle, pdfauthor
 
985
        # TODO?: pdfcreator, pdfproducer, pdfsubject, pdfkeywords
 
986
        self.pdfinfo = []
 
987
        self.pdfauthor = []
 
988
 
 
989
        # Stack of section counters so that we don't have to use_latex_toc.
 
990
        # This will grow and shrink as processing occurs.
 
991
        # Initialized for potential first-level sections.
 
992
        self._section_number = [0]
 
993
 
 
994
        # The current stack of enumerations so that we can expand
 
995
        # them into a compound enumeration.
 
996
        self._enumeration_counters = []
 
997
        # The maximum number of enumeration counters we've used.
 
998
        # If we go beyond this number, we need to create a new
 
999
        # counter; otherwise, just reuse an old one.
 
1000
        self._max_enumeration_counters = 0
 
1001
 
 
1002
        self._bibitems = []
 
1003
 
 
1004
        # object for a table while proccessing.
 
1005
        self.table_stack = []
 
1006
        self.active_table = Table(self, 'longtable', settings.table_style)
 
1007
 
 
1008
        # Where to collect the output of visitor methods (default: body)
 
1009
        self.out = self.body
 
1010
        self.out_stack = []  # stack of output collectors
 
1011
 
 
1012
        # Process settings
 
1013
        # ~~~~~~~~~~~~~~~~
 
1014
 
 
1015
        # Static requirements
 
1016
        # TeX font encoding
 
1017
        if self.font_encoding:
 
1018
            encodings = [r'\usepackage[%s]{fontenc}' % self.font_encoding]
 
1019
        else:
 
1020
            encodings = [r'%\usepackage[OT1]{fontenc}'] # just a comment
 
1021
        # Docutils' output-encoding => TeX input encoding:
 
1022
        if self.latex_encoding != 'ascii':
 
1023
            encodings.append(r'\usepackage[%s]{inputenc}'
 
1024
                             % self.latex_encoding)
 
1025
        self.requirements['_static'] = '\n'.join(
 
1026
              encodings + [
 
1027
              r'\usepackage{ifthen}',
 
1028
              # multi-language support (language is in document options)
 
1029
              '\\usepackage{babel}%s' % self.babel.setup,
 
1030
              ])
 
1031
        # page layout with typearea (if there are relevant document options)
 
1032
        if (settings.documentclass.find('scr') == -1 and
 
1033
            (self.d_options.find('DIV') != -1 or
 
1034
             self.d_options.find('BCOR') != -1)):
 
1035
            self.requirements['typearea'] = r'\usepackage{typearea}'
 
1036
 
 
1037
        # Stylesheets
 
1038
        # get list of style sheets from settings
 
1039
        styles = utils.get_stylesheet_list(settings)
 
1040
        # adapt path if --stylesheet_path is used
 
1041
        if settings.stylesheet_path and not(self.embed_stylesheet):
 
1042
            styles = [utils.relative_path(settings._destination, sheet)
 
1043
                      for sheet in styles]
 
1044
        for sheet in styles:
 
1045
            (base, ext) = os.path.splitext(sheet)
 
1046
            is_package = ext in ['.sty', '']
 
1047
            if self.embed_stylesheet:
 
1048
                if is_package:
 
1049
                    sheet = base + '.sty' # adapt package name
 
1050
                    # wrap in \makeatletter, \makeatother
 
1051
                    wrapper = PreambleCmds.embedded_package_wrapper
 
1052
                else:
 
1053
                    wrapper = '%% embedded stylesheet: %s\n%s'
 
1054
                settings.record_dependencies.add(sheet)
 
1055
                self.stylesheet.append(wrapper %
 
1056
                    (sheet, io.FileInput(source_path=sheet, encoding='utf-8').read()))
 
1057
            else: # link to style sheet
 
1058
                if is_package:
 
1059
                    self.stylesheet.append(r'\usepackage{%s}' % base)
 
1060
                else:
 
1061
                    self.stylesheet.append(r'\input{%s}' % sheet)
 
1062
 
 
1063
        # PDF setup
 
1064
        if self.hyperlink_color == '0':
 
1065
            self.hyperlink_color = 'black'
 
1066
            self.colorlinks = 'false'
 
1067
        else:
 
1068
            self.colorlinks = 'true'
 
1069
 
 
1070
        # LaTeX Toc
 
1071
        # include all supported sections in toc and PDF bookmarks
 
1072
        # (or use documentclass-default (as currently))?
 
1073
        ## if self.use_latex_toc:
 
1074
        ##    self.requirements['tocdepth'] = (r'\setcounter{tocdepth}{%d}' %
 
1075
        ##                                     len(self.d_class.sections))
 
1076
 
 
1077
        # LaTeX section numbering
 
1078
        if not self.settings.sectnum_xform: # section numbering by LaTeX:
 
1079
            # sectnum_depth:
 
1080
            #   None  "sectnum" directive without depth arg -> LaTeX default
 
1081
            #   0     no "sectnum" directive -> no section numbers
 
1082
            #   else  value of the "depth" argument: translate to LaTeX level
 
1083
            #         -1  part    (0 with "article" document class)
 
1084
            #          0  chapter (missing in "article" document class)
 
1085
            #          1  section
 
1086
            #          2  subsection
 
1087
            #          3  subsubsection
 
1088
            #          4  paragraph
 
1089
            #          5  subparagraph
 
1090
            if settings.sectnum_depth is not None:
 
1091
                # limit to supported levels
 
1092
                sectnum_depth = min(settings.sectnum_depth,
 
1093
                                    len(self.d_class.sections))
 
1094
                # adjust to document class and use_part_section settings
 
1095
                if 'chapter' in  self.d_class.sections:
 
1096
                    sectnum_depth -= 1
 
1097
                if self.d_class.sections[0] == 'part':
 
1098
                    sectnum_depth -= 1
 
1099
                self.requirements['sectnum_depth'] = (
 
1100
                    r'\setcounter{secnumdepth}{%d}' % sectnum_depth)
 
1101
            # start with specified number:
 
1102
            if (hasattr(settings, 'sectnum_start') and
 
1103
                settings.sectnum_start != 1):
 
1104
                self.requirements['sectnum_start'] = (
 
1105
                    r'\setcounter{%s}{%d}' % (self.d_class.sections[0],
 
1106
                                              settings.sectnum_start-1))
 
1107
            # currently ignored (configure in a stylesheet):
 
1108
            ## settings.sectnum_prefix
 
1109
            ## settings.sectnum_suffix
 
1110
 
 
1111
 
 
1112
    # Auxiliary Methods
 
1113
    # -----------------
 
1114
 
 
1115
    def to_latex_encoding(self,docutils_encoding):
 
1116
        """Translate docutils encoding name into LaTeX's.
 
1117
 
 
1118
        Default method is remove "-" and "_" chars from docutils_encoding.
 
1119
        """
 
1120
        tr = {  'iso-8859-1': 'latin1',     # west european
 
1121
                'iso-8859-2': 'latin2',     # east european
 
1122
                'iso-8859-3': 'latin3',     # esperanto, maltese
 
1123
                'iso-8859-4': 'latin4',     # north european, scandinavian, baltic
 
1124
                'iso-8859-5': 'iso88595',   # cyrillic (ISO)
 
1125
                'iso-8859-9': 'latin5',     # turkish
 
1126
                'iso-8859-15': 'latin9',    # latin9, update to latin1.
 
1127
                'mac_cyrillic': 'maccyr',   # cyrillic (on Mac)
 
1128
                'windows-1251': 'cp1251',   # cyrillic (on Windows)
 
1129
                'koi8-r': 'koi8-r',         # cyrillic (Russian)
 
1130
                'koi8-u': 'koi8-u',         # cyrillic (Ukrainian)
 
1131
                'windows-1250': 'cp1250',   #
 
1132
                'windows-1252': 'cp1252',   #
 
1133
                'us-ascii': 'ascii',        # ASCII (US)
 
1134
                # unmatched encodings
 
1135
                #'': 'applemac',
 
1136
                #'': 'ansinew',  # windows 3.1 ansi
 
1137
                #'': 'ascii',    # ASCII encoding for the range 32--127.
 
1138
                #'': 'cp437',    # dos latin us
 
1139
                #'': 'cp850',    # dos latin 1
 
1140
                #'': 'cp852',    # dos latin 2
 
1141
                #'': 'decmulti',
 
1142
                #'': 'latin10',
 
1143
                #'iso-8859-6': ''   # arabic
 
1144
                #'iso-8859-7': ''   # greek
 
1145
                #'iso-8859-8': ''   # hebrew
 
1146
                #'iso-8859-10': ''   # latin6, more complete iso-8859-4
 
1147
             }
 
1148
        encoding = docutils_encoding.lower()
 
1149
        if encoding in tr:
 
1150
            return tr[encoding]
 
1151
        # convert: latin-1, latin_1, utf-8 and similar things
 
1152
        encoding = encoding.replace('_', '').replace('-', '')
 
1153
        # strip the error handler
 
1154
        return encoding.split(':')[0]
 
1155
 
 
1156
    def language_label(self, docutil_label):
 
1157
        return self.language.labels[docutil_label]
 
1158
 
 
1159
    def ensure_math(self, text):
 
1160
        if not hasattr(self, 'ensure_math_re'):
 
1161
            chars = { # lnot,pm,twosuperior,threesuperior,mu,onesuperior,times,div
 
1162
                     'latin1' : '\xac\xb1\xb2\xb3\xb5\xb9\xd7\xf7' , # ¬±²³µ¹×÷
 
1163
                     # TODO?: use texcomp instead.
 
1164
                    }
 
1165
            self.ensure_math_re = re.compile('([%s])' % chars['latin1'])
 
1166
        text = self.ensure_math_re.sub(r'\\ensuremath{\1}', text)
 
1167
        return text
 
1168
 
 
1169
    def encode(self, text):
 
1170
        """Return text with 'problematic' characters escaped.
 
1171
 
 
1172
        Escape the ten special printing characters ``# $ % & ~ _ ^ \ { }``,
 
1173
        square brackets ``[ ]``, double quotes and (in OT1) ``< | >``.
 
1174
 
 
1175
        Separate ``-`` (and more in literal text) to prevent input ligatures.
 
1176
 
 
1177
        Translate non-supported Unicode characters.
 
1178
        """
 
1179
        if self.verbatim:
 
1180
            return text
 
1181
        # Separate compound characters, e.g. '--' to '-{}-'.
 
1182
        separate_chars = '-'
 
1183
        # In monospace-font, we also separate ',,', '``' and "''" and some
 
1184
        # other characters which can't occur in non-literal text.
 
1185
        if self.literal:
 
1186
            separate_chars += ',`\'"<>'
 
1187
        # LaTeX encoding maps:
 
1188
        special_chars = {
 
1189
            ord('#'): ur'\#',
 
1190
            ord('$'): ur'\$',
 
1191
            ord('%'): ur'\%',
 
1192
            ord('&'): ur'\&',
 
1193
            ord('~'): ur'\textasciitilde{}',
 
1194
            ord('_'): ur'\_',
 
1195
            ord('^'): ur'\textasciicircum{}',
 
1196
            ord('\\'): ur'\textbackslash{}',
 
1197
            ord('{'): ur'\{',
 
1198
            ord('}'): ur'\}',
 
1199
        # Square brackets are ordinary chars and cannot be escaped with '\',
 
1200
        # so we put them in a group '{[}'. (Alternative: ensure that all
 
1201
        # macros with optional arguments are terminated with {} and text
 
1202
        # inside any optional argument is put in a group ``[{text}]``).
 
1203
        # Commands with optional args inside an optional arg must be put
 
1204
        # in a group, e.g. ``\item[{\hyperref[label]{text}}]``.
 
1205
            ord('['): ur'{[}',
 
1206
            ord(']'): ur'{]}'
 
1207
        }
 
1208
        # Unicode chars that are not recognized by LaTeX's utf8 encoding
 
1209
        unsupported_unicode_chars = {
 
1210
            0x00A0: ur'~', # NO-BREAK SPACE
 
1211
            0x00AD: ur'\-', # SOFT HYPHEN
 
1212
            #
 
1213
            0x2011: ur'\hbox{-}', # NON-BREAKING HYPHEN
 
1214
            0x21d4: ur'$\Leftrightarrow$',
 
1215
            # Docutils footnote symbols:
 
1216
            0x2660: ur'$\spadesuit$',
 
1217
            0x2663: ur'$\clubsuit$',
 
1218
        }
 
1219
        # Unicode chars that are recognized by LaTeX's utf8 encoding
 
1220
        unicode_chars = {
 
1221
            0x200C: ur'\textcompwordmark', # ZERO WIDTH NON-JOINER
 
1222
            0x2013: ur'\textendash{}',
 
1223
            0x2014: ur'\textemdash{}',
 
1224
            0x2018: ur'\textquoteleft{}',
 
1225
            0x2019: ur'\textquoteright{}',
 
1226
            0x201A: ur'\quotesinglbase{}', # SINGLE LOW-9 QUOTATION MARK
 
1227
            0x201C: ur'\textquotedblleft{}',
 
1228
            0x201D: ur'\textquotedblright{}',
 
1229
            0x201E: ur'\quotedblbase{}', # DOUBLE LOW-9 QUOTATION MARK
 
1230
            0x2030: ur'\textperthousand{}',   # PER MILLE SIGN
 
1231
            0x2031: ur'\textpertenthousand{}', # PER TEN THOUSAND SIGN
 
1232
            0x2039: ur'\guilsinglleft{}',
 
1233
            0x203A: ur'\guilsinglright{}',
 
1234
            0x2423: ur'\textvisiblespace{}',  # OPEN BOX
 
1235
            0x2020: ur'\dag{}',
 
1236
            0x2021: ur'\ddag{}',
 
1237
            0x2026: ur'\dots{}',
 
1238
            0x2122: ur'\texttrademark{}',
 
1239
        }
 
1240
        # Unicode chars that require a feature/package to render
 
1241
        pifont_chars = {
 
1242
            0x2665: ur'\ding{170}',     # black heartsuit
 
1243
            0x2666: ur'\ding{169}',     # black diamondsuit
 
1244
        }
 
1245
        # recognized with 'utf8', if textcomp is loaded
 
1246
        textcomp_chars = {
 
1247
            # Latin-1 Supplement
 
1248
            0x00a2: ur'\textcent{}',          # ¢ CENT SIGN
 
1249
            0x00a4: ur'\textcurrency{}',      # ¤ CURRENCY SYMBOL
 
1250
            0x00a5: ur'\textyen{}',           # ¥ YEN SIGN
 
1251
            0x00a6: ur'\textbrokenbar{}',     # ¦ BROKEN BAR
 
1252
            0x00a7: ur'\textsection{}',       # § SECTION SIGN
 
1253
            0x00a8: ur'\textasciidieresis{}', # ¨ DIAERESIS
 
1254
            0x00a9: ur'\textcopyright{}',     # © COPYRIGHT SIGN
 
1255
            0x00aa: ur'\textordfeminine{}',   # ª FEMININE ORDINAL INDICATOR
 
1256
            0x00ac: ur'\textlnot{}',          # ¬ NOT SIGN
 
1257
            0x00ae: ur'\textregistered{}',    # ® REGISTERED SIGN
 
1258
            0x00af: ur'\textasciimacron{}',   # ¯ MACRON
 
1259
            0x00b0: ur'\textdegree{}',        # ° DEGREE SIGN
 
1260
            0x00b1: ur'\textpm{}',            # ± PLUS-MINUS SIGN
 
1261
            0x00b2: ur'\texttwosuperior{}',   # ² SUPERSCRIPT TWO
 
1262
            0x00b3: ur'\textthreesuperior{}', # ³ SUPERSCRIPT THREE
 
1263
            0x00b4: ur'\textasciiacute{}',    # ´ ACUTE ACCENT
 
1264
            0x00b5: ur'\textmu{}',            # µ MICRO SIGN
 
1265
            0x00b6: ur'\textparagraph{}',     # ¶ PILCROW SIGN # not equal to \textpilcrow
 
1266
            0x00b9: ur'\textonesuperior{}',   # ¹ SUPERSCRIPT ONE
 
1267
            0x00ba: ur'\textordmasculine{}',  # º MASCULINE ORDINAL INDICATOR
 
1268
            0x00bc: ur'\textonequarter{}',    # 1/4 FRACTION
 
1269
            0x00bd: ur'\textonehalf{}',       # 1/2 FRACTION
 
1270
            0x00be: ur'\textthreequarters{}', # 3/4 FRACTION
 
1271
            0x00d7: ur'\texttimes{}',         # × MULTIPLICATION SIGN
 
1272
            0x00f7: ur'\textdiv{}',           # ÷ DIVISION SIGN
 
1273
            #
 
1274
            0x0192: ur'\textflorin{}',        # LATIN SMALL LETTER F WITH HOOK
 
1275
            0x02b9: ur'\textasciiacute{}',    # MODIFIER LETTER PRIME
 
1276
            0x02ba: ur'\textacutedbl{}',      # MODIFIER LETTER DOUBLE PRIME
 
1277
            0x2016: ur'\textbardbl{}',        # DOUBLE VERTICAL LINE
 
1278
            0x2022: ur'\textbullet{}',        # BULLET
 
1279
            0x2032: ur'\textasciiacute{}',    # PRIME
 
1280
            0x2033: ur'\textacutedbl{}',      # DOUBLE PRIME
 
1281
            0x2035: ur'\textasciigrave{}',    # REVERSED PRIME
 
1282
            0x2036: ur'\textgravedbl{}',      # REVERSED DOUBLE PRIME
 
1283
            0x203b: ur'\textreferencemark{}', # REFERENCE MARK
 
1284
            0x203d: ur'\textinterrobang{}',   # INTERROBANG
 
1285
            0x2044: ur'\textfractionsolidus{}', # FRACTION SLASH
 
1286
            0x2045: ur'\textlquill{}',        # LEFT SQUARE BRACKET WITH QUILL
 
1287
            0x2046: ur'\textrquill{}',        # RIGHT SQUARE BRACKET WITH QUILL
 
1288
            0x2052: ur'\textdiscount{}',      # COMMERCIAL MINUS SIGN
 
1289
            0x20a1: ur'\textcolonmonetary{}', # COLON SIGN
 
1290
            0x20a3: ur'\textfrenchfranc{}',   # FRENCH FRANC SIGN
 
1291
            0x20a4: ur'\textlira{}',          # LIRA SIGN
 
1292
            0x20a6: ur'\textnaira{}',         # NAIRA SIGN
 
1293
            0x20a9: ur'\textwon{}',           # WON SIGN
 
1294
            0x20ab: ur'\textdong{}',          # DONG SIGN
 
1295
            0x20ac: ur'\texteuro{}',          # EURO SIGN
 
1296
            0x20b1: ur'\textpeso{}',          # PESO SIGN
 
1297
            0x20b2: ur'\textguarani{}',       # GUARANI SIGN
 
1298
            0x2103: ur'\textcelsius{}',       # DEGREE CELSIUS
 
1299
            0x2116: ur'\textnumero{}',        # NUMERO SIGN
 
1300
            0x2117: ur'\textcircledP{}',      # SOUND RECORDING COYRIGHT
 
1301
            0x211e: ur'\textrecipe{}',        # PRESCRIPTION TAKE
 
1302
            0x2120: ur'\textservicemark{}',   # SERVICE MARK
 
1303
            0x2122: ur'\texttrademark{}',     # TRADE MARK SIGN
 
1304
            0x2126: ur'\textohm{}',           # OHM SIGN
 
1305
            0x2127: ur'\textmho{}',           # INVERTED OHM SIGN
 
1306
            0x212e: ur'\textestimated{}',     # ESTIMATED SYMBOL
 
1307
            0x2190: ur'\textleftarrow{}',     # LEFTWARDS ARROW
 
1308
            0x2191: ur'\textuparrow{}',       # UPWARDS ARROW
 
1309
            0x2192: ur'\textrightarrow{}',    # RIGHTWARDS ARROW
 
1310
            0x2193: ur'\textdownarrow{}',     # DOWNWARDS ARROW
 
1311
            0x2212: ur'\textminus{}',         # MINUS SIGN
 
1312
            0x2217: ur'\textasteriskcentered{}', # ASTERISK OPERATOR
 
1313
            0x221a: ur'\textsurd{}',          # SQUARE ROOT
 
1314
            0x2422: ur'\textblank{}',         # BLANK SYMBOL
 
1315
            0x25e6: ur'\textopenbullet{}',    # WHITE BULLET
 
1316
            0x25ef: ur'\textbigcircle{}',     # LARGE CIRCLE
 
1317
            0x266a: ur'\textmusicalnote{}',   # EIGHTH NOTE
 
1318
            0x26ad: ur'\textmarried{}',       # MARRIAGE SYMBOL
 
1319
            0x26ae: ur'\textdivorced{}',      # DIVORCE SYMBOL
 
1320
            0x27e8: ur'\textlangle{}',        # MATHEMATICAL LEFT ANGLE BRACKET
 
1321
            0x27e9: ur'\textrangle{}',        # MATHEMATICAL RIGHT ANGLE BRACKET
 
1322
        }
 
1323
        # TODO: greek alphabet ... ?
 
1324
        # see also LaTeX codec
 
1325
        # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252124
 
1326
        # and unimap.py from TeXML
 
1327
 
 
1328
        # set up the translation table:
 
1329
        table = special_chars
 
1330
        # keep the underscore in citation references
 
1331
        if self.inside_citation_reference_label:
 
1332
            del(table[ord('_')])
 
1333
        # Workarounds for OT1 font-encoding
 
1334
        if self.font_encoding in ['OT1', '']:
 
1335
            # * out-of-order characters in cmtt
 
1336
            if self.literal:
 
1337
                # replace underscore by underlined blank,
 
1338
                # because this has correct width.
 
1339
                table[ord('_')] = u'\\underline{~}'
 
1340
                # the backslash doesn't work, so we use a mirrored slash.
 
1341
                # \reflectbox is provided by graphicx:
 
1342
                self.requirements['graphicx'] = self.graphicx_package
 
1343
                table[ord('\\')] = ur'\reflectbox{/}'
 
1344
            # * ``< | >`` come out as different chars (except for cmtt):
 
1345
            else:
 
1346
                table[ord('|')] = ur'\textbar{}'
 
1347
                table[ord('<')] = ur'\textless{}'
 
1348
                table[ord('>')] = ur'\textgreater{}'
 
1349
        if self.insert_non_breaking_blanks:
 
1350
            table[ord(' ')] = ur'~'
 
1351
        if self.literal:
 
1352
            # double quotes are 'active' in some languages
 
1353
            table[ord('"')] = self.babel.literal_double_quote
 
1354
        else:
 
1355
            text = self.babel.quote_quotes(text)
 
1356
        # Unicode chars:
 
1357
        table.update(unsupported_unicode_chars)
 
1358
        table.update(pifont_chars)
 
1359
        if not self.latex_encoding.startswith('utf8'):
 
1360
            table.update(unicode_chars)
 
1361
            table.update(textcomp_chars)
 
1362
        # Characters that require a feature/package to render
 
1363
        for ch in text:
 
1364
            if ord(ch) in pifont_chars:
 
1365
                self.requirements['pifont'] = '\\usepackage{pifont}'
 
1366
            if ord(ch) in textcomp_chars:
 
1367
                self.requirements['textcomp'] = PreambleCmds.textcomp
 
1368
 
 
1369
        text = text.translate(table)
 
1370
 
 
1371
        # Break up input ligatures
 
1372
        for char in separate_chars * 2:
 
1373
            # Do it twice ("* 2") because otherwise we would replace
 
1374
            # '---' by '-{}--'.
 
1375
            text = text.replace(char + char, char + '{}' + char)
 
1376
        # Literal line breaks (in address or literal blocks):
 
1377
        if self.insert_newline:
 
1378
            # for blank lines, insert a protected space, to avoid
 
1379
            # ! LaTeX Error: There's no line here to end.
 
1380
            textlines = [line + '~'*(not line.lstrip())
 
1381
                         for line in text.split('\n')]
 
1382
            text = '\\\\\n'.join(textlines)
 
1383
        if self.literal and not self.insert_non_breaking_blanks:
 
1384
            # preserve runs of spaces but allow wrapping
 
1385
            text = text.replace('  ', ' ~')
 
1386
        if not self.latex_encoding.startswith('utf8'):
 
1387
            text = self.ensure_math(text)
 
1388
        return text
 
1389
 
 
1390
    def attval(self, text,
 
1391
               whitespace=re.compile('[\n\r\t\v\f]')):
 
1392
        """Cleanse, encode, and return attribute value text."""
 
1393
        return self.encode(whitespace.sub(' ', text))
 
1394
 
 
1395
    # TODO: is this used anywhere? (update or delete)
 
1396
    ## def astext(self):
 
1397
    ##     """Assemble document parts and return as string."""
 
1398
    ##     head = '\n'.join(self.head_prefix + self.stylesheet + self.head)
 
1399
    ##     body = ''.join(self.body_prefix  + self.body + self.body_suffix)
 
1400
    ##     return head + '\n' + body
 
1401
 
 
1402
    def is_inline(self, node):
 
1403
        """Check whether a node represents an inline element"""
 
1404
        return isinstance(node.parent, nodes.TextElement)
 
1405
 
 
1406
    def append_hypertargets(self, node):
 
1407
        """Append hypertargets for all ids of `node`"""
 
1408
        # hypertarget places the anchor at the target's baseline,
 
1409
        # so we raise it explicitely
 
1410
        self.out.append('%\n'.join(['\\raisebox{1em}{\\hypertarget{%s}{}}' %
 
1411
                                    id for id in node['ids']]))
 
1412
 
 
1413
    def ids_to_labels(self, node, set_anchor=True):
 
1414
        """Return list of label definitions for all ids of `node`
 
1415
 
 
1416
        If `set_anchor` is True, an anchor is set with \phantomsection.
 
1417
        """
 
1418
        labels = ['\\label{%s}' % id for id in node.get('ids', [])]
 
1419
        if set_anchor and labels:
 
1420
            labels.insert(0, '\\phantomsection')
 
1421
        return labels
 
1422
 
 
1423
    def push_output_collector(self, new_out):
 
1424
        self.out_stack.append(self.out)
 
1425
        self.out = new_out
 
1426
 
 
1427
    def pop_output_collector(self):
 
1428
        self.out = self.out_stack.pop()
 
1429
 
 
1430
    # Visitor methods
 
1431
    # ---------------
 
1432
 
 
1433
    def visit_Text(self, node):
 
1434
        self.out.append(self.encode(node.astext()))
 
1435
 
 
1436
    def depart_Text(self, node):
 
1437
        pass
 
1438
 
 
1439
    def visit_address(self, node):
 
1440
        self.visit_docinfo_item(node, 'address')
 
1441
 
 
1442
    def depart_address(self, node):
 
1443
        self.depart_docinfo_item(node)
 
1444
 
 
1445
    def visit_admonition(self, node):
 
1446
        self.fallbacks['admonition'] = PreambleCmds.admonition
 
1447
        if 'error' in node['classes']:
 
1448
            self.fallbacks['error'] = PreambleCmds.error
 
1449
        # strip the generic 'admonition' from the list of classes
 
1450
        node['classes'] = [cls for cls in node['classes']
 
1451
                           if cls != 'admonition']
 
1452
        self.out.append('\n\\DUadmonition[%s]{\n' % ','.join(node['classes']))
 
1453
 
 
1454
    def depart_admonition(self, node=None):
 
1455
        self.out.append('}\n')
 
1456
 
 
1457
    def visit_author(self, node):
 
1458
        self.visit_docinfo_item(node, 'author')
 
1459
 
 
1460
    def depart_author(self, node):
 
1461
        self.depart_docinfo_item(node)
 
1462
 
 
1463
    def visit_authors(self, node):
 
1464
        # not used: visit_author is called anyway for each author.
 
1465
        pass
 
1466
 
 
1467
    def depart_authors(self, node):
 
1468
        pass
 
1469
 
 
1470
    def visit_block_quote(self, node):
 
1471
        self.out.append( '%\n\\begin{quote}\n')
 
1472
 
 
1473
    def depart_block_quote(self, node):
 
1474
        self.out.append( '\n\\end{quote}\n')
 
1475
 
 
1476
    def visit_bullet_list(self, node):
 
1477
        if self.is_toc_list:
 
1478
            self.out.append( '%\n\\begin{list}{}{}\n' )
 
1479
        else:
 
1480
            self.out.append( '%\n\\begin{itemize}\n' )
 
1481
 
 
1482
    def depart_bullet_list(self, node):
 
1483
        if self.is_toc_list:
 
1484
            self.out.append( '\n\\end{list}\n' )
 
1485
        else:
 
1486
            self.out.append( '\n\\end{itemize}\n' )
 
1487
 
 
1488
    def visit_superscript(self, node):
 
1489
        self.out.append(r'\textsuperscript{')
 
1490
        if node['classes']:
 
1491
            self.visit_inline(node)
 
1492
 
 
1493
    def depart_superscript(self, node):
 
1494
        if node['classes']:
 
1495
            self.depart_inline(node)
 
1496
        self.out.append('}')
 
1497
 
 
1498
    def visit_subscript(self, node):
 
1499
        self.out.append(r'\textsubscript{') # requires `fixltx2e`
 
1500
        if node['classes']:
 
1501
            self.visit_inline(node)
 
1502
 
 
1503
    def depart_subscript(self, node):
 
1504
        if node['classes']:
 
1505
            self.depart_inline(node)
 
1506
        self.out.append('}')
 
1507
 
 
1508
    def visit_caption(self, node):
 
1509
        self.out.append( '\\caption{' )
 
1510
 
 
1511
    def depart_caption(self, node):
 
1512
        self.out.append('}\n')
 
1513
 
 
1514
    def visit_title_reference(self, node):
 
1515
        self.fallbacks['titlereference'] = PreambleCmds.titlereference
 
1516
        self.out.append(r'\DUroletitlereference{')
 
1517
        if node['classes']:
 
1518
            self.visit_inline(node)
 
1519
 
 
1520
    def depart_title_reference(self, node):
 
1521
        if node['classes']:
 
1522
            self.depart_inline(node)
 
1523
        self.out.append( '}' )
 
1524
 
 
1525
    def visit_citation(self, node):
 
1526
        # TODO maybe use cite bibitems
 
1527
        if self._use_latex_citations:
 
1528
            self.push_output_collector([])
 
1529
        else:
 
1530
            # TODO: do we need these?
 
1531
            ## self.requirements['~fnt_floats'] = PreambleCmds.footnote_floats
 
1532
            self.out.append(r'\begin{figure}[b]')
 
1533
            self.append_hypertargets(node)
 
1534
 
 
1535
    def depart_citation(self, node):
 
1536
        if self._use_latex_citations:
 
1537
            label = self.out[0]
 
1538
            text = ''.join(self.out[1:])
 
1539
            self._bibitems.append([label, text])
 
1540
            self.pop_output_collector()
 
1541
        else:
 
1542
            self.out.append('\\end{figure}\n')
 
1543
 
 
1544
    def visit_citation_reference(self, node):
 
1545
        if self._use_latex_citations:
 
1546
            if not self.inside_citation_reference_label:
 
1547
                self.out.append(r'\cite{')
 
1548
                self.inside_citation_reference_label = 1
 
1549
            else:
 
1550
                assert self.body[-1] in (' ', '\n'),\
 
1551
                        'unexpected non-whitespace while in reference label'
 
1552
                del self.body[-1]
 
1553
        else:
 
1554
            href = ''
 
1555
            if 'refid' in node:
 
1556
                href = node['refid']
 
1557
            elif 'refname' in node:
 
1558
                href = self.document.nameids[node['refname']]
 
1559
            self.out.append('[\\hyperlink{%s}{' % href)
 
1560
 
 
1561
    def depart_citation_reference(self, node):
 
1562
        if self._use_latex_citations:
 
1563
            followup_citation = False
 
1564
            # check for a following citation separated by a space or newline
 
1565
            next_siblings = node.traverse(descend=0, siblings=1,
 
1566
                                          include_self=0)
 
1567
            if len(next_siblings) > 1:
 
1568
                next = next_siblings[0]
 
1569
                if (isinstance(next, nodes.Text) and
 
1570
                    next.astext() in (' ', '\n')):
 
1571
                    if next_siblings[1].__class__ == node.__class__:
 
1572
                        followup_citation = True
 
1573
            if followup_citation:
 
1574
                self.out.append(',')
 
1575
            else:
 
1576
                self.out.append('}')
 
1577
                self.inside_citation_reference_label = False
 
1578
        else:
 
1579
            self.out.append('}]')
 
1580
 
 
1581
    def visit_classifier(self, node):
 
1582
        self.out.append( '(\\textbf{' )
 
1583
 
 
1584
    def depart_classifier(self, node):
 
1585
        self.out.append( '})\n' )
 
1586
 
 
1587
    def visit_colspec(self, node):
 
1588
        self.active_table.visit_colspec(node)
 
1589
 
 
1590
    def depart_colspec(self, node):
 
1591
        pass
 
1592
 
 
1593
    def visit_comment(self, node):
 
1594
        # Precede every line with a comment sign, wrap in newlines
 
1595
        self.out.append('\n%% %s\n' % node.astext().replace('\n', '\n% '))
 
1596
        raise nodes.SkipNode
 
1597
 
 
1598
    def depart_comment(self, node):
 
1599
        pass
 
1600
 
 
1601
    def visit_compound(self, node):
 
1602
        pass
 
1603
 
 
1604
    def depart_compound(self, node):
 
1605
        pass
 
1606
 
 
1607
    def visit_contact(self, node):
 
1608
        self.visit_docinfo_item(node, 'contact')
 
1609
 
 
1610
    def depart_contact(self, node):
 
1611
        self.depart_docinfo_item(node)
 
1612
 
 
1613
    def visit_container(self, node):
 
1614
        pass
 
1615
 
 
1616
    def depart_container(self, node):
 
1617
        pass
 
1618
 
 
1619
    def visit_copyright(self, node):
 
1620
        self.visit_docinfo_item(node, 'copyright')
 
1621
 
 
1622
    def depart_copyright(self, node):
 
1623
        self.depart_docinfo_item(node)
 
1624
 
 
1625
    def visit_date(self, node):
 
1626
        self.visit_docinfo_item(node, 'date')
 
1627
 
 
1628
    def depart_date(self, node):
 
1629
        self.depart_docinfo_item(node)
 
1630
 
 
1631
    def visit_decoration(self, node):
 
1632
        # header and footer
 
1633
        pass
 
1634
 
 
1635
    def depart_decoration(self, node):
 
1636
        pass
 
1637
 
 
1638
    def visit_definition(self, node):
 
1639
        pass
 
1640
 
 
1641
    def depart_definition(self, node):
 
1642
        self.out.append('\n')
 
1643
 
 
1644
    def visit_definition_list(self, node):
 
1645
        self.out.append( '%\n\\begin{description}\n' )
 
1646
 
 
1647
    def depart_definition_list(self, node):
 
1648
        self.out.append( '\\end{description}\n' )
 
1649
 
 
1650
    def visit_definition_list_item(self, node):
 
1651
        pass
 
1652
 
 
1653
    def depart_definition_list_item(self, node):
 
1654
        pass
 
1655
 
 
1656
    def visit_description(self, node):
 
1657
        self.out.append(' ')
 
1658
 
 
1659
    def depart_description(self, node):
 
1660
        pass
 
1661
 
 
1662
    def visit_docinfo(self, node):
 
1663
        self.push_output_collector(self.docinfo)
 
1664
 
 
1665
    def depart_docinfo(self, node):
 
1666
        self.pop_output_collector()
 
1667
        # Some itmes (e.g. author) end up at other places
 
1668
        if self.docinfo:
 
1669
            # tabularx: automatic width of columns, no page breaks allowed.
 
1670
            self.requirements['tabularx'] = r'\usepackage{tabularx}'
 
1671
            self.fallbacks['_providelength'] = PreambleCmds.providelength
 
1672
            self.fallbacks['docinfo'] = PreambleCmds.docinfo
 
1673
            #
 
1674
            self.docinfo.insert(0, '\n% Docinfo\n'
 
1675
                                '\\begin{center}\n'
 
1676
                                '\\begin{tabularx}{\\DUdocinfowidth}{lX}\n')
 
1677
            self.docinfo.append('\\end{tabularx}\n'
 
1678
                                '\\end{center}\n')
 
1679
 
 
1680
    def visit_docinfo_item(self, node, name):
 
1681
        if name == 'author':
 
1682
            self.pdfauthor.append(self.attval(node.astext()))
 
1683
        if self.use_latex_docinfo:
 
1684
            if name in ('author', 'organization', 'contact', 'address'):
 
1685
                # We attach these to the last author.  If any of them precedes
 
1686
                # the first author, put them in a separate "author" group
 
1687
                # (in lack of better semantics).
 
1688
                if name == 'author' or not self.author_stack:
 
1689
                    self.author_stack.append([])
 
1690
                if name == 'address':   # newlines are meaningful
 
1691
                    self.insert_newline = 1
 
1692
                    text = self.encode(node.astext())
 
1693
                    self.insert_newline = False
 
1694
                else:
 
1695
                    text = self.attval(node.astext())
 
1696
                self.author_stack[-1].append(text)
 
1697
                raise nodes.SkipNode
 
1698
            elif name == 'date':
 
1699
                self.date.append(self.attval(node.astext()))
 
1700
                raise nodes.SkipNode
 
1701
        self.out.append('\\textbf{%s}: &\n\t' % self.language_label(name))
 
1702
        if name == 'address':
 
1703
            self.insert_newline = 1
 
1704
            self.out.append('{\\raggedright\n')
 
1705
            self.context.append(' } \\\\\n')
 
1706
        else:
 
1707
            self.context.append(' \\\\\n')
 
1708
 
 
1709
    def depart_docinfo_item(self, node):
 
1710
        self.out.append(self.context.pop())
 
1711
        # for address we did set insert_newline
 
1712
        self.insert_newline = False
 
1713
 
 
1714
    def visit_doctest_block(self, node):
 
1715
        self.visit_literal_block(node)
 
1716
 
 
1717
    def depart_doctest_block(self, node):
 
1718
        self.depart_literal_block(node)
 
1719
 
 
1720
    def visit_document(self, node):
 
1721
        # titled document?
 
1722
        if (self.use_latex_docinfo or len(node) and
 
1723
            isinstance(node[0], nodes.title)):
 
1724
            self.title_labels += self.ids_to_labels(node)
 
1725
 
 
1726
    def depart_document(self, node):
 
1727
        # Complete header with information gained from walkabout
 
1728
        # a) conditional requirements (before style sheet)
 
1729
        self.requirements = self.requirements.sortedvalues()
 
1730
        # b) coditional fallback definitions (after style sheet)
 
1731
        self.fallbacks = self.fallbacks.sortedvalues()
 
1732
        # c) PDF properties
 
1733
        self.pdfsetup.append(PreambleCmds.linking % (self.colorlinks,
 
1734
                                                     self.hyperlink_color,
 
1735
                                                     self.hyperlink_color))
 
1736
        if self.pdfauthor:
 
1737
            authors = self.author_separator.join(self.pdfauthor)
 
1738
            self.pdfinfo.append('  pdfauthor={%s}' % authors)
 
1739
        if self.pdfinfo:
 
1740
            self.pdfsetup += [r'\hypersetup{'] + self.pdfinfo + ['}']
 
1741
        # Complete body
 
1742
        # a) document title (part 'body_prefix'):
 
1743
        # NOTE: Docutils puts author/date into docinfo, so normally
 
1744
        #       we do not want LaTeX author/date handling (via \maketitle).
 
1745
        #       To deactivate it, we add \title, \author, \date,
 
1746
        #       even if the arguments are empty strings.
 
1747
        if self.title or self.author_stack or self.date:
 
1748
            authors = ['\\\\\n'.join(author_entry)
 
1749
                       for author_entry in self.author_stack]
 
1750
            title = [''.join(self.title)] + self.title_labels
 
1751
            if self.subtitle:
 
1752
                title += [r'\\ % subtitle',
 
1753
                             r'\large{%s}' % ''.join(self.subtitle)
 
1754
                         ] + self.subtitle_labels
 
1755
            self.body_pre_docinfo.append(PreambleCmds.documenttitle % (
 
1756
                '%\n  '.join(title),
 
1757
                ' \\and\n'.join(authors),
 
1758
                ', '.join(self.date)))
 
1759
        # b) bibliography
 
1760
        # TODO insertion point of bibliography should be configurable.
 
1761
        if self._use_latex_citations and len(self._bibitems)>0:
 
1762
            if not self.bibtex:
 
1763
                widest_label = ''
 
1764
                for bi in self._bibitems:
 
1765
                    if len(widest_label)<len(bi[0]):
 
1766
                        widest_label = bi[0]
 
1767
                self.out.append('\n\\begin{thebibliography}{%s}\n' %
 
1768
                                 widest_label)
 
1769
                for bi in self._bibitems:
 
1770
                    # cite_key: underscores must not be escaped
 
1771
                    cite_key = bi[0].replace(r'\_','_')
 
1772
                    self.out.append('\\bibitem[%s]{%s}{%s}\n' %
 
1773
                                     (bi[0], cite_key, bi[1]))
 
1774
                self.out.append('\\end{thebibliography}\n')
 
1775
            else:
 
1776
                self.out.append('\n\\bibliographystyle{%s}\n' %
 
1777
                                self.bibtex[0])
 
1778
                self.out.append('\\bibliography{%s}\n' % self.bibtex[1])
 
1779
        # c) make sure to generate a toc file if needed for local contents:
 
1780
        if 'minitoc' in self.requirements and not self.has_latex_toc:
 
1781
            self.out.append('\n\\faketableofcontents % for local ToCs\n')
 
1782
 
 
1783
    def visit_emphasis(self, node):
 
1784
        self.out.append('\\emph{')
 
1785
        if node['classes']:
 
1786
            self.visit_inline(node)
 
1787
 
 
1788
    def depart_emphasis(self, node):
 
1789
        if node['classes']:
 
1790
            self.depart_inline(node)
 
1791
        self.out.append('}')
 
1792
 
 
1793
    def visit_entry(self, node):
 
1794
        self.active_table.visit_entry()
 
1795
        # cell separation
 
1796
        # BUG: the following fails, with more than one multirow
 
1797
        # starting in the second column (or later) see
 
1798
        # ../../../test/functional/input/data/latex.txt
 
1799
        if self.active_table.get_entry_number() == 1:
 
1800
            # if the first row is a multirow, this actually is the second row.
 
1801
            # this gets hairy if rowspans follow each other.
 
1802
            if self.active_table.get_rowspan(0):
 
1803
                count = 0
 
1804
                while self.active_table.get_rowspan(count):
 
1805
                    count += 1
 
1806
                    self.out.append(' & ')
 
1807
                self.active_table.visit_entry() # increment cell count
 
1808
        else:
 
1809
            self.out.append(' & ')
 
1810
        # multirow, multicolumn
 
1811
        # IN WORK BUG TODO HACK continues here
 
1812
        # multirow in LaTeX simply will enlarge the cell over several rows
 
1813
        # (the following n if n is positive, the former if negative).
 
1814
        if 'morerows' in node and 'morecols' in node:
 
1815
            raise NotImplementedError('Cells that '
 
1816
            'span multiple rows *and* columns are not supported, sorry.')
 
1817
        if 'morerows' in node:
 
1818
            self.requirements['multirow'] = r'\usepackage{multirow}'
 
1819
            count = node['morerows'] + 1
 
1820
            self.active_table.set_rowspan(
 
1821
                                self.active_table.get_entry_number()-1,count)
 
1822
            self.out.append('\\multirow{%d}{%s}{%%' %
 
1823
                            (count,self.active_table.get_column_width()))
 
1824
            self.context.append('}')
 
1825
        elif 'morecols' in node:
 
1826
            # the vertical bar before column is missing if it is the first
 
1827
            # column. the one after always.
 
1828
            if self.active_table.get_entry_number() == 1:
 
1829
                bar1 = self.active_table.get_vertical_bar()
 
1830
            else:
 
1831
                bar1 = ''
 
1832
            count = node['morecols'] + 1
 
1833
            self.out.append('\\multicolumn{%d}{%sl%s}{' %
 
1834
                    (count, bar1, self.active_table.get_vertical_bar()))
 
1835
            self.context.append('}')
 
1836
        else:
 
1837
            self.context.append('')
 
1838
 
 
1839
        # header / not header
 
1840
        if isinstance(node.parent.parent, nodes.thead):
 
1841
            self.out.append('\\textbf{%')
 
1842
            self.context.append('}')
 
1843
        elif self.active_table.is_stub_column():
 
1844
            self.out.append('\\textbf{')
 
1845
            self.context.append('}')
 
1846
        else:
 
1847
            self.context.append('')
 
1848
 
 
1849
    def depart_entry(self, node):
 
1850
        self.out.append(self.context.pop()) # header / not header
 
1851
        self.out.append(self.context.pop()) # multirow/column
 
1852
        # if following row is spanned from above.
 
1853
        if self.active_table.get_rowspan(self.active_table.get_entry_number()):
 
1854
           self.out.append(' & ')
 
1855
           self.active_table.visit_entry() # increment cell count
 
1856
 
 
1857
    def visit_row(self, node):
 
1858
        self.active_table.visit_row()
 
1859
 
 
1860
    def depart_row(self, node):
 
1861
        self.out.extend(self.active_table.depart_row())
 
1862
 
 
1863
    def visit_enumerated_list(self, node):
 
1864
        # We create our own enumeration list environment.
 
1865
        # This allows to set the style and starting value
 
1866
        # and unlimited nesting.
 
1867
        enum_style = {'arabic':'arabic',
 
1868
                'loweralpha':'alph',
 
1869
                'upperalpha':'Alph',
 
1870
                'lowerroman':'roman',
 
1871
                'upperroman':'Roman' }
 
1872
        enum_suffix = ''
 
1873
        if 'suffix' in node:
 
1874
            enum_suffix = node['suffix']
 
1875
        enum_prefix = ''
 
1876
        if 'prefix' in node:
 
1877
            enum_prefix = node['prefix']
 
1878
        if self.compound_enumerators:
 
1879
            pref = ''
 
1880
            if self.section_prefix_for_enumerators and self.section_level:
 
1881
                for i in range(self.section_level):
 
1882
                    pref += '%d.' % self._section_number[i]
 
1883
                pref = pref[:-1] + self.section_enumerator_separator
 
1884
                enum_prefix += pref
 
1885
            for ctype, cname in self._enumeration_counters:
 
1886
                enum_prefix += '\\%s{%s}.' % (ctype, cname)
 
1887
        enum_type = 'arabic'
 
1888
        if 'enumtype' in node:
 
1889
            enum_type = node['enumtype']
 
1890
        if enum_type in enum_style:
 
1891
            enum_type = enum_style[enum_type]
 
1892
 
 
1893
        counter_name = 'listcnt%d' % len(self._enumeration_counters)
 
1894
        self._enumeration_counters.append((enum_type, counter_name))
 
1895
        # If we haven't used this counter name before, then create a
 
1896
        # new counter; otherwise, reset & reuse the old counter.
 
1897
        if len(self._enumeration_counters) > self._max_enumeration_counters:
 
1898
            self._max_enumeration_counters = len(self._enumeration_counters)
 
1899
            self.out.append('\\newcounter{%s}\n' % counter_name)
 
1900
        else:
 
1901
            self.out.append('\\setcounter{%s}{0}\n' % counter_name)
 
1902
 
 
1903
        self.out.append('\\begin{list}{%s\\%s{%s}%s}\n' %
 
1904
                        (enum_prefix,enum_type,counter_name,enum_suffix))
 
1905
        self.out.append('{\n')
 
1906
        self.out.append('\\usecounter{%s}\n' % counter_name)
 
1907
        # set start after usecounter, because it initializes to zero.
 
1908
        if 'start' in node:
 
1909
            self.out.append('\\addtocounter{%s}{%d}\n' %
 
1910
                            (counter_name,node['start']-1))
 
1911
        ## set rightmargin equal to leftmargin
 
1912
        self.out.append('\\setlength{\\rightmargin}{\\leftmargin}\n')
 
1913
        self.out.append('}\n')
 
1914
 
 
1915
    def depart_enumerated_list(self, node):
 
1916
        self.out.append('\\end{list}\n')
 
1917
        self._enumeration_counters.pop()
 
1918
 
 
1919
    def visit_field(self, node):
 
1920
        # real output is done in siblings: _argument, _body, _name
 
1921
        pass
 
1922
 
 
1923
    def depart_field(self, node):
 
1924
        self.out.append('\n')
 
1925
        ##self.out.append('%[depart_field]\n')
 
1926
 
 
1927
    def visit_field_argument(self, node):
 
1928
        self.out.append('%[visit_field_argument]\n')
 
1929
 
 
1930
    def depart_field_argument(self, node):
 
1931
        self.out.append('%[depart_field_argument]\n')
 
1932
 
 
1933
    def visit_field_body(self, node):
 
1934
        pass
 
1935
 
 
1936
    def depart_field_body(self, node):
 
1937
        if self.out is self.docinfo:
 
1938
            self.out.append(r'\\')
 
1939
 
 
1940
    def visit_field_list(self, node):
 
1941
        if self.out is not self.docinfo:
 
1942
            self.fallbacks['fieldlist'] = PreambleCmds.fieldlist
 
1943
            self.out.append('%\n\\begin{DUfieldlist}\n')
 
1944
 
 
1945
    def depart_field_list(self, node):
 
1946
        if self.out is not self.docinfo:
 
1947
            self.out.append('\\end{DUfieldlist}\n')
 
1948
 
 
1949
    def visit_field_name(self, node):
 
1950
        if self.out is self.docinfo:
 
1951
            self.out.append('\\textbf{')
 
1952
        else:
 
1953
            # Commands with optional args inside an optional arg must be put
 
1954
            # in a group, e.g. ``\item[{\hyperref[label]{text}}]``.
 
1955
            self.out.append('\\item[{')
 
1956
 
 
1957
    def depart_field_name(self, node):
 
1958
        if self.out is self.docinfo:
 
1959
            self.out.append('}: &')
 
1960
        else:
 
1961
            self.out.append(':}]')
 
1962
 
 
1963
    def visit_figure(self, node):
 
1964
        self.requirements['float_settings'] = PreambleCmds.float_settings
 
1965
        # ! the 'align' attribute should set "outer alignment" !
 
1966
        # For "inner alignment" use LaTeX default alignment (similar to HTML)
 
1967
        ## if ('align' not in node.attributes or
 
1968
        ##     node.attributes['align'] == 'center'):
 
1969
        ##     align = '\n\\centering'
 
1970
        ##     align_end = ''
 
1971
        ## else:
 
1972
        ##     # TODO non vertical space for other alignments.
 
1973
        ##     align = '\\begin{flush%s}' % node.attributes['align']
 
1974
        ##     align_end = '\\end{flush%s}' % node.attributes['align']
 
1975
        ## self.out.append( '\\begin{figure}%s\n' % align )
 
1976
        ## self.context.append( '%s\\end{figure}\n' % align_end )
 
1977
        self.out.append('\\begin{figure}')
 
1978
        if node.get('ids'):
 
1979
            self.out += ['\n'] + self.ids_to_labels(node)
 
1980
 
 
1981
    def depart_figure(self, node):
 
1982
        self.out.append('\\end{figure}\n')
 
1983
 
 
1984
    def visit_footer(self, node):
 
1985
        self.push_output_collector([])
 
1986
        self.out.append(r'\newcommand{\DUfooter}{')
 
1987
 
 
1988
    def depart_footer(self, node):
 
1989
        self.out.append('}')
 
1990
        self.requirements['~footer'] = ''.join(self.out)
 
1991
        self.pop_output_collector()
 
1992
 
 
1993
    def visit_footnote(self, node):
 
1994
        try:
 
1995
            backref = node['backrefs'][0]
 
1996
        except IndexError:
 
1997
            backref = node['ids'][0] # no backref, use self-ref instead
 
1998
        if self.settings.figure_footnotes:
 
1999
            self.requirements['~fnt_floats'] = PreambleCmds.footnote_floats
 
2000
            self.out.append('\\begin{figure}[b]')
 
2001
            self.append_hypertargets(node)
 
2002
            if node.get('id') == node.get('name'):  # explicite label
 
2003
                self.out += self.ids_to_labels(node)
 
2004
        elif self.docutils_footnotes:
 
2005
            self.fallbacks['footnotes'] = PreambleCmds.footnotes
 
2006
            num,text = node.astext().split(None,1)
 
2007
            if self.settings.footnote_references == 'brackets':
 
2008
                num = '[%s]' % num
 
2009
            self.out.append('%%\n\\DUfootnotetext{%s}{%s}{%s}{' %
 
2010
                            (node['ids'][0], backref, self.encode(num)))
 
2011
            if node['ids'] == node['names']:
 
2012
                self.out += self.ids_to_labels(node)
 
2013
            # mask newline to prevent spurious whitespace:
 
2014
            self.out.append('%')
 
2015
        ## else:  # TODO: "real" LaTeX \footnote{}s
 
2016
 
 
2017
    def depart_footnote(self, node):
 
2018
        if self.figure_footnotes:
 
2019
            self.out.append('\\end{figure}\n')
 
2020
        else:
 
2021
            self.out.append('}\n')
 
2022
 
 
2023
    def visit_footnote_reference(self, node):
 
2024
        href = ''
 
2025
        if 'refid' in node:
 
2026
            href = node['refid']
 
2027
        elif 'refname' in node:
 
2028
            href = self.document.nameids[node['refname']]
 
2029
        # if not self.docutils_footnotes:
 
2030
            # TODO: insert footnote content at (or near) this place
 
2031
            # print "footnote-ref to", node['refid']
 
2032
            # footnotes = (self.document.footnotes +
 
2033
            #              self.document.autofootnotes +
 
2034
            #              self.document.symbol_footnotes)
 
2035
            # for footnote in footnotes:
 
2036
            #     # print footnote['ids']
 
2037
            #     if node.get('refid', '') in footnote['ids']:
 
2038
            #         print 'matches', footnote['ids']
 
2039
        format = self.settings.footnote_references
 
2040
        if format == 'brackets':
 
2041
            self.append_hypertargets(node)
 
2042
            self.out.append('\\hyperlink{%s}{[' % href)
 
2043
            self.context.append(']}')
 
2044
        else:
 
2045
            self.fallbacks['footnotes'] = PreambleCmds.footnotes
 
2046
            self.out.append(r'\DUfootnotemark{%s}{%s}{' %
 
2047
                            (node['ids'][0], href))
 
2048
            self.context.append('}')
 
2049
 
 
2050
    def depart_footnote_reference(self, node):
 
2051
        self.out.append(self.context.pop())
 
2052
 
 
2053
    # footnote/citation label
 
2054
    def label_delim(self, node, bracket, superscript):
 
2055
        if isinstance(node.parent, nodes.footnote):
 
2056
            if not self.figure_footnotes:
 
2057
                raise nodes.SkipNode
 
2058
            if self.settings.footnote_references == 'brackets':
 
2059
                self.out.append(bracket)
 
2060
            else:
 
2061
                self.out.append(superscript)
 
2062
        else:
 
2063
            assert isinstance(node.parent, nodes.citation)
 
2064
            if not self._use_latex_citations:
 
2065
                self.out.append(bracket)
 
2066
 
 
2067
    def visit_label(self, node):
 
2068
        """footnote or citation label: in brackets or as superscript"""
 
2069
        self.label_delim(node, '[', '\\textsuperscript{')
 
2070
 
 
2071
    def depart_label(self, node):
 
2072
        self.label_delim(node, ']', '}')
 
2073
 
 
2074
    # elements generated by the framework e.g. section numbers.
 
2075
    def visit_generated(self, node):
 
2076
        pass
 
2077
 
 
2078
    def depart_generated(self, node):
 
2079
        pass
 
2080
 
 
2081
    def visit_header(self, node):
 
2082
        self.push_output_collector([])
 
2083
        self.out.append(r'\newcommand{\DUheader}{')
 
2084
 
 
2085
    def depart_header(self, node):
 
2086
        self.out.append('}')
 
2087
        self.requirements['~header'] = ''.join(self.out)
 
2088
        self.pop_output_collector()
 
2089
 
 
2090
    def to_latex_length(self, length_str):
 
2091
        """Convert string with rst lenght to LaTeX"""
 
2092
        match = re.match('(\d*\.?\d*)\s*(\S*)', length_str)
 
2093
        if not match:
 
2094
            return length_str
 
2095
        value, unit = match.groups()[:2]
 
2096
        # no unit or "DTP" points (called 'bp' in TeX):
 
2097
        if unit in ('', 'pt'):
 
2098
            length_str = '%sbp' % value
 
2099
        # percentage: relate to current line width
 
2100
        elif unit == '%':
 
2101
            length_str = '%.3f\\linewidth' % (float(value)/100.0)
 
2102
        return length_str
 
2103
 
 
2104
    def visit_image(self, node):
 
2105
        self.requirements['graphicx'] = self.graphicx_package
 
2106
        attrs = node.attributes
 
2107
        # Add image URI to dependency list, assuming that it's
 
2108
        # referring to a local file.
 
2109
        self.settings.record_dependencies.add(attrs['uri'])
 
2110
        # alignment defaults:
 
2111
        if not 'align' in attrs:
 
2112
            # Set default align of image in a figure to 'center'
 
2113
            if isinstance(node.parent, nodes.figure):
 
2114
                attrs['align'] = 'center'
 
2115
            # query 'align-*' class argument
 
2116
            for cls in node['classes']:
 
2117
                if cls.startswith('align-'):
 
2118
                    attrs['align'] = cls.split('-')[1]
 
2119
        # pre- and postfix (prefix inserted in reverse order)
 
2120
        pre = []
 
2121
        post = []
 
2122
        include_graphics_options = []
 
2123
        display_style = ('block-', 'inline-')[self.is_inline(node)]
 
2124
        align_codes = {
 
2125
            # inline images: by default latex aligns the bottom.
 
2126
            'bottom': ('', ''),
 
2127
            'middle': (r'\raisebox{-0.5\height}{', '}'),
 
2128
            'top':    (r'\raisebox{-\height}{', '}'),
 
2129
            # block level images:
 
2130
            'center': (r'\noindent\makebox[\textwidth][c]{', '}'),
 
2131
            'left':   (r'\noindent{', r'\hfill}'),
 
2132
            'right':  (r'\noindent{\hfill', '}'),}
 
2133
        if 'align' in attrs:
 
2134
            try:
 
2135
                align_code = align_codes[attrs['align']]
 
2136
                pre.append(align_code[0])
 
2137
                post.append(align_code[1])
 
2138
            except KeyError:
 
2139
                pass                    # TODO: warn?
 
2140
        if 'height' in attrs:
 
2141
            include_graphics_options.append('height=%s' %
 
2142
                            self.to_latex_length(attrs['height']))
 
2143
        if 'scale' in attrs:
 
2144
            include_graphics_options.append('scale=%f' %
 
2145
                                            (attrs['scale'] / 100.0))
 
2146
        if 'width' in attrs:
 
2147
            include_graphics_options.append('width=%s' %
 
2148
                            self.to_latex_length(attrs['width']))
 
2149
        if not self.is_inline(node):
 
2150
            pre.append('\n')
 
2151
            post.append('\n')
 
2152
        pre.reverse()
 
2153
        self.out.extend(pre)
 
2154
        options = ''
 
2155
        if include_graphics_options:
 
2156
            options = '[%s]' % (','.join(include_graphics_options))
 
2157
        self.out.append('\\includegraphics%s{%s}' % (options, attrs['uri']))
 
2158
        self.out.extend(post)
 
2159
 
 
2160
    def depart_image(self, node):
 
2161
        if node.get('ids'):
 
2162
            self.out += self.ids_to_labels(node) + ['\n']
 
2163
 
 
2164
    def visit_interpreted(self, node):
 
2165
        # @@@ Incomplete, pending a proper implementation on the
 
2166
        # Parser/Reader end.
 
2167
        self.visit_literal(node)
 
2168
 
 
2169
    def depart_interpreted(self, node):
 
2170
        self.depart_literal(node)
 
2171
 
 
2172
    def visit_legend(self, node):
 
2173
        self.fallbacks['legend'] = PreambleCmds.legend
 
2174
        self.out.append('\\begin{DUlegend}')
 
2175
 
 
2176
    def depart_legend(self, node):
 
2177
        self.out.append('\\end{DUlegend}\n')
 
2178
 
 
2179
    def visit_line(self, node):
 
2180
        self.out.append('\item[] ')
 
2181
 
 
2182
    def depart_line(self, node):
 
2183
        self.out.append('\n')
 
2184
 
 
2185
    def visit_line_block(self, node):
 
2186
        self.fallbacks['_providelength'] = PreambleCmds.providelength
 
2187
        self.fallbacks['lineblock'] = PreambleCmds.lineblock
 
2188
        if isinstance(node.parent, nodes.line_block):
 
2189
            self.out.append('\\item[]\n'
 
2190
                             '\\begin{DUlineblock}{\\DUlineblockindent}\n')
 
2191
        else:
 
2192
            self.out.append('\n\\begin{DUlineblock}{0em}\n')
 
2193
 
 
2194
    def depart_line_block(self, node):
 
2195
        self.out.append('\\end{DUlineblock}\n')
 
2196
 
 
2197
    def visit_list_item(self, node):
 
2198
        self.out.append('\n\\item ')
 
2199
 
 
2200
    def depart_list_item(self, node):
 
2201
        pass
 
2202
 
 
2203
    def visit_literal(self, node):
 
2204
        self.literal = True
 
2205
        self.out.append('\\texttt{')
 
2206
        if node['classes']:
 
2207
            self.visit_inline(node)
 
2208
 
 
2209
    def depart_literal(self, node):
 
2210
        self.literal = False
 
2211
        if node['classes']:
 
2212
            self.depart_inline(node)
 
2213
        self.out.append('}')
 
2214
 
 
2215
    # Literal blocks are used for '::'-prefixed literal-indented
 
2216
    # blocks of text, where the inline markup is not recognized,
 
2217
    # but are also the product of the "parsed-literal" directive,
 
2218
    # where the markup is respected.
 
2219
    #
 
2220
    # In both cases, we want to use a typewriter/monospaced typeface.
 
2221
    # For "real" literal-blocks, we can use \verbatim, while for all
 
2222
    # the others we must use \mbox or \alltt.
 
2223
    #
 
2224
    # We can distinguish between the two kinds by the number of
 
2225
    # siblings that compose this node: if it is composed by a
 
2226
    # single element, it's either
 
2227
    # * a real one,
 
2228
    # * a parsed-literal that does not contain any markup, or
 
2229
    # * a parsed-literal containing just one markup construct.
 
2230
    def is_plaintext(self, node):
 
2231
        """Check whether a node can be typeset verbatim"""
 
2232
        return (len(node) == 1) and isinstance(node[0], nodes.Text)
 
2233
 
 
2234
    def visit_literal_block(self, node):
 
2235
        """Render a literal block."""
 
2236
        # environments and packages to typeset literal blocks
 
2237
        packages = {'listing': r'\usepackage{moreverb}',
 
2238
                    'lstlisting': r'\usepackage{listings}',
 
2239
                    'Verbatim': r'\usepackage{fancyvrb}',
 
2240
                    # 'verbatim': '',
 
2241
                    'verbatimtab': r'\usepackage{moreverb}'}
 
2242
 
 
2243
        if not self.active_table.is_open():
 
2244
            # no quote inside tables, to avoid vertical space between
 
2245
            # table border and literal block.
 
2246
            # BUG: fails if normal text preceeds the literal block.
 
2247
            self.out.append('%\n\\begin{quote}')
 
2248
            self.context.append('\n\\end{quote}\n')
 
2249
        else:
 
2250
            self.out.append('\n')
 
2251
            self.context.append('\n')
 
2252
        if self.literal_block_env != '' and self.is_plaintext(node):
 
2253
            self.requirements['literal_block'] = packages.get(
 
2254
                                                  self.literal_block_env, '')
 
2255
            self.verbatim = True
 
2256
            self.out.append('\\begin{%s}%s\n' % (self.literal_block_env,
 
2257
                                                 self.literal_block_options))
 
2258
        else:
 
2259
            self.literal = True
 
2260
            self.insert_newline = True
 
2261
            self.insert_non_breaking_blanks = True
 
2262
            self.out.append('{\\ttfamily \\raggedright \\noindent\n')
 
2263
 
 
2264
    def depart_literal_block(self, node):
 
2265
        if self.verbatim:
 
2266
            self.out.append('\n\\end{%s}\n' % self.literal_block_env)
 
2267
            self.verbatim = False
 
2268
        else:
 
2269
            self.out.append('\n}')
 
2270
            self.insert_non_breaking_blanks = False
 
2271
            self.insert_newline = False
 
2272
            self.literal = False
 
2273
        self.out.append(self.context.pop())
 
2274
 
 
2275
    ## def visit_meta(self, node):
 
2276
    ##     self.out.append('[visit_meta]\n')
 
2277
        # TODO: set keywords for pdf?
 
2278
        # But:
 
2279
        #  The reStructuredText "meta" directive creates a "pending" node,
 
2280
        #  which contains knowledge that the embedded "meta" node can only
 
2281
        #  be handled by HTML-compatible writers. The "pending" node is
 
2282
        #  resolved by the docutils.transforms.components.Filter transform,
 
2283
        #  which checks that the calling writer supports HTML; if it doesn't,
 
2284
        #  the "pending" node (and enclosed "meta" node) is removed from the
 
2285
        #  document.
 
2286
        #  --- docutils/docs/peps/pep-0258.html#transformer
 
2287
 
 
2288
    ## def depart_meta(self, node):
 
2289
    ##     self.out.append('[depart_meta]\n')
 
2290
 
 
2291
    def visit_option(self, node):
 
2292
        if self.context[-1]:
 
2293
            # this is not the first option
 
2294
            self.out.append(', ')
 
2295
 
 
2296
    def depart_option(self, node):
 
2297
        # flag tha the first option is done.
 
2298
        self.context[-1] += 1
 
2299
 
 
2300
    def visit_option_argument(self, node):
 
2301
        """Append the delimiter betweeen an option and its argument to body."""
 
2302
        self.out.append(node.get('delimiter', ' '))
 
2303
 
 
2304
    def depart_option_argument(self, node):
 
2305
        pass
 
2306
 
 
2307
    def visit_option_group(self, node):
 
2308
        self.out.append('\n\\item[')
 
2309
        # flag for first option
 
2310
        self.context.append(0)
 
2311
 
 
2312
    def depart_option_group(self, node):
 
2313
        self.context.pop() # the flag
 
2314
        self.out.append('] ')
 
2315
 
 
2316
    def visit_option_list(self, node):
 
2317
        self.fallbacks['_providelength'] = PreambleCmds.providelength
 
2318
        self.fallbacks['optionlist'] = PreambleCmds.optionlist
 
2319
        self.out.append('%\n\\begin{DUoptionlist}\n')
 
2320
 
 
2321
    def depart_option_list(self, node):
 
2322
        self.out.append('\n\\end{DUoptionlist}\n')
 
2323
 
 
2324
    def visit_option_list_item(self, node):
 
2325
        pass
 
2326
 
 
2327
    def depart_option_list_item(self, node):
 
2328
        pass
 
2329
 
 
2330
    def visit_option_string(self, node):
 
2331
        ##self.out.append(self.starttag(node, 'span', '', CLASS='option'))
 
2332
        pass
 
2333
 
 
2334
    def depart_option_string(self, node):
 
2335
        ##self.out.append('</span>')
 
2336
        pass
 
2337
 
 
2338
    def visit_organization(self, node):
 
2339
        self.visit_docinfo_item(node, 'organization')
 
2340
 
 
2341
    def depart_organization(self, node):
 
2342
        self.depart_docinfo_item(node)
 
2343
 
 
2344
    def visit_paragraph(self, node):
 
2345
        # no newline if the paragraph is first in a list item
 
2346
        if ((isinstance(node.parent, nodes.list_item) or
 
2347
             isinstance(node.parent, nodes.description)) and
 
2348
            node is node.parent[0]):
 
2349
            return
 
2350
        index = node.parent.index(node)
 
2351
        if (isinstance(node.parent, nodes.compound) and
 
2352
            index > 0 and
 
2353
            not isinstance(node.parent[index - 1], nodes.paragraph) and
 
2354
            not isinstance(node.parent[index - 1], nodes.compound)):
 
2355
            return
 
2356
        self.out.append('\n')
 
2357
        if node.get('ids'):
 
2358
            self.out += self.ids_to_labels(node) + ['\n']
 
2359
 
 
2360
    def depart_paragraph(self, node):
 
2361
        self.out.append('\n')
 
2362
 
 
2363
    def visit_problematic(self, node):
 
2364
        self.requirements['color'] = PreambleCmds.color
 
2365
        self.out.append('%\n')
 
2366
        self.append_hypertargets(node)
 
2367
        self.out.append(r'\hyperlink{%s}{\textbf{\color{red}' % node['refid'])
 
2368
 
 
2369
    def depart_problematic(self, node):
 
2370
        self.out.append('}}')
 
2371
 
 
2372
    def visit_raw(self, node):
 
2373
        if not 'latex' in node.get('format', '').split():
 
2374
            raise nodes.SkipNode
 
2375
        if node['classes']:
 
2376
            self.visit_inline(node)
 
2377
        # append "as-is" skipping any LaTeX-encoding
 
2378
        self.verbatim = True
 
2379
 
 
2380
    def depart_raw(self, node):
 
2381
        self.verbatim = False
 
2382
        if node['classes']:
 
2383
            self.depart_inline(node)
 
2384
 
 
2385
    def has_unbalanced_braces(self, string):
 
2386
        """Test whether there are unmatched '{' or '}' characters."""
 
2387
        level = 0
 
2388
        for ch in string:
 
2389
            if ch == '{':
 
2390
                level += 1
 
2391
            if ch == '}':
 
2392
                level -= 1
 
2393
            if level < 0:
 
2394
                return True
 
2395
        return level != 0
 
2396
 
 
2397
    def visit_reference(self, node):
 
2398
        # We need to escape #, \, and % if we use the URL in a command.
 
2399
        special_chars = {ord('#'): ur'\#',
 
2400
                         ord('%'): ur'\%',
 
2401
                         ord('\\'): ur'\\',
 
2402
                        }
 
2403
        # external reference (URL)
 
2404
        if 'refuri' in node:
 
2405
            href = unicode(node['refuri']).translate(special_chars)
 
2406
            # problematic chars double caret and unbalanced braces:
 
2407
            if href.find('^^') != -1 or self.has_unbalanced_braces(href):
 
2408
                self.error(
 
2409
                    'External link "%s" not supported by LaTeX.\n'
 
2410
                    ' (Must not contain "^^" or unbalanced braces.)' % href)
 
2411
            if node['refuri'] == node.astext():
 
2412
                self.out.append(r'\url{%s}' % href)
 
2413
                raise nodes.SkipNode
 
2414
            self.out.append(r'\href{%s}{' % href)
 
2415
            return
 
2416
        # internal reference
 
2417
        if 'refid' in node:
 
2418
            href = node['refid']
 
2419
        elif 'refname' in node:
 
2420
            href = self.document.nameids[node['refname']]
 
2421
        else:
 
2422
            raise AssertionError('Unknown reference.')
 
2423
        if not self.is_inline(node):
 
2424
            self.out.append('\n')
 
2425
        self.out.append('\\hyperref[%s]{' % href)
 
2426
        if self._reference_label:
 
2427
            self.out.append('\\%s{%s}}' %
 
2428
                            (self._reference_label, href.replace('#', '')))
 
2429
            raise nodes.SkipNode
 
2430
 
 
2431
    def depart_reference(self, node):
 
2432
        self.out.append('}')
 
2433
        if not self.is_inline(node):
 
2434
            self.out.append('\n')
 
2435
 
 
2436
    def visit_revision(self, node):
 
2437
        self.visit_docinfo_item(node, 'revision')
 
2438
 
 
2439
    def depart_revision(self, node):
 
2440
        self.depart_docinfo_item(node)
 
2441
 
 
2442
    def visit_section(self, node):
 
2443
        self.section_level += 1
 
2444
        # Initialize counter for potential subsections:
 
2445
        self._section_number.append(0)
 
2446
        # Counter for this section's level (initialized by parent section):
 
2447
        self._section_number[self.section_level - 1] += 1
 
2448
 
 
2449
    def depart_section(self, node):
 
2450
        # Remove counter for potential subsections:
 
2451
        self._section_number.pop()
 
2452
        self.section_level -= 1
 
2453
 
 
2454
    def visit_sidebar(self, node):
 
2455
        self.requirements['color'] = PreambleCmds.color
 
2456
        self.fallbacks['sidebar'] = PreambleCmds.sidebar
 
2457
        self.out.append('\n\\DUsidebar{\n')
 
2458
 
 
2459
    def depart_sidebar(self, node):
 
2460
        self.out.append('}\n')
 
2461
 
 
2462
    attribution_formats = {'dash': ('---', ''),
 
2463
                           'parentheses': ('(', ')'),
 
2464
                           'parens': ('(', ')'),
 
2465
                           'none': ('', '')}
 
2466
 
 
2467
    def visit_attribution(self, node):
 
2468
        prefix, suffix = self.attribution_formats[self.settings.attribution]
 
2469
        self.out.append('\n\\begin{flushright}\n')
 
2470
        self.out.append(prefix)
 
2471
        self.context.append(suffix)
 
2472
 
 
2473
    def depart_attribution(self, node):
 
2474
        self.out.append(self.context.pop() + '\n')
 
2475
        self.out.append('\\end{flushright}\n')
 
2476
 
 
2477
    def visit_status(self, node):
 
2478
        self.visit_docinfo_item(node, 'status')
 
2479
 
 
2480
    def depart_status(self, node):
 
2481
        self.depart_docinfo_item(node)
 
2482
 
 
2483
    def visit_strong(self, node):
 
2484
        self.out.append('\\textbf{')
 
2485
        if node['classes']:
 
2486
            self.visit_inline(node)
 
2487
 
 
2488
    def depart_strong(self, node):
 
2489
        if node['classes']:
 
2490
            self.depart_inline(node)
 
2491
        self.out.append('}')
 
2492
 
 
2493
    def visit_substitution_definition(self, node):
 
2494
        raise nodes.SkipNode
 
2495
 
 
2496
    def visit_substitution_reference(self, node):
 
2497
        self.unimplemented_visit(node)
 
2498
 
 
2499
    def visit_subtitle(self, node):
 
2500
        if isinstance(node.parent, nodes.document):
 
2501
            self.push_output_collector(self.subtitle)
 
2502
            self.subtitle_labels += self.ids_to_labels(node, set_anchor=False)
 
2503
        # section subtitle: "starred" (no number, not in ToC)
 
2504
        elif isinstance(node.parent, nodes.section):
 
2505
            self.out.append(r'\%s*{' %
 
2506
                             self.d_class.section(self.section_level + 1))
 
2507
        else:
 
2508
            self.fallbacks['subtitle'] = PreambleCmds.subtitle
 
2509
            self.out.append('\n\\DUsubtitle[%s]{' % node.parent.tagname)
 
2510
 
 
2511
    def depart_subtitle(self, node):
 
2512
        if isinstance(node.parent, nodes.document):
 
2513
            self.pop_output_collector()
 
2514
        else:
 
2515
            self.out.append('}\n')
 
2516
 
 
2517
    def visit_system_message(self, node):
 
2518
        self.requirements['color'] = PreambleCmds.color
 
2519
        self.fallbacks['title'] = PreambleCmds.title
 
2520
        node['classes'] = ['system-message']
 
2521
        self.visit_admonition(node)
 
2522
        self.out.append('\\DUtitle[system-message]{system-message}\n')
 
2523
        self.append_hypertargets(node)
 
2524
        try:
 
2525
            line = ', line~%s' % node['line']
 
2526
        except KeyError:
 
2527
            line = ''
 
2528
        self.out.append('\n\n{\color{red}%s/%s} in \\texttt{%s}%s\n' %
 
2529
                         (node['type'], node['level'],
 
2530
                          self.encode(node['source']), line))
 
2531
        if len(node['backrefs']) == 1:
 
2532
            self.out.append('\n\\hyperlink{%s}{' % node['backrefs'][0])
 
2533
            self.context.append('}')
 
2534
        else:
 
2535
            backrefs = ['\\hyperlink{%s}{%d}' % (href, i+1)
 
2536
                        for (i, href) in enumerate(node['backrefs'])]
 
2537
            self.context.append('backrefs: ' + ' '.join(backrefs))
 
2538
 
 
2539
    def depart_system_message(self, node):
 
2540
        self.out.append(self.context.pop())
 
2541
        self.depart_admonition()
 
2542
 
 
2543
    def visit_table(self, node):
 
2544
        self.requirements['table'] = PreambleCmds.table
 
2545
        if self.active_table.is_open():
 
2546
            self.table_stack.append(self.active_table)
 
2547
            # nesting longtable does not work (e.g. 2007-04-18)
 
2548
            self.active_table = Table(self,'tabular',self.settings.table_style)
 
2549
        self.active_table.open()
 
2550
        for cls in node['classes']:
 
2551
            self.active_table.set_table_style(cls)
 
2552
        if self.active_table._table_style == 'booktabs':
 
2553
            self.requirements['booktabs'] = r'\usepackage{booktabs}'
 
2554
        self.out.append('\n' + self.active_table.get_opening())
 
2555
 
 
2556
    def depart_table(self, node):
 
2557
        self.out.append(self.active_table.get_closing() + '\n')
 
2558
        self.active_table.close()
 
2559
        if len(self.table_stack)>0:
 
2560
            self.active_table = self.table_stack.pop()
 
2561
        else:
 
2562
            self.active_table.set_table_style(self.settings.table_style)
 
2563
        # Insert hyperlabel after (long)table, as
 
2564
        # other places (beginning, caption) result in LaTeX errors.
 
2565
        if node.get('ids'):
 
2566
            self.out += self.ids_to_labels(node, set_anchor=False) + ['\n']
 
2567
 
 
2568
    def visit_target(self, node):
 
2569
        # Skip indirect targets:
 
2570
        if ('refuri' in node       # external hyperlink
 
2571
            or 'refid' in node     # resolved internal link
 
2572
            or 'refname' in node): # unresolved internal link
 
2573
            ## self.out.append('%% %s\n' % node)   # for debugging
 
2574
            return
 
2575
        self.out.append('%\n')
 
2576
        # do we need an anchor (\phantomsection)?
 
2577
        set_anchor = not(isinstance(node.parent, nodes.caption) or
 
2578
                         isinstance(node.parent, nodes.title))
 
2579
        # TODO: where else can/must we omit the \phantomsection?
 
2580
        self.out += self.ids_to_labels(node, set_anchor)
 
2581
 
 
2582
    def depart_target(self, node):
 
2583
        pass
 
2584
 
 
2585
    def visit_tbody(self, node):
 
2586
        # BUG write preamble if not yet done (colspecs not [])
 
2587
        # for tables without heads.
 
2588
        if not self.active_table.get('preamble written'):
 
2589
            self.visit_thead(None)
 
2590
            self.depart_thead(None)
 
2591
 
 
2592
    def depart_tbody(self, node):
 
2593
        pass
 
2594
 
 
2595
    def visit_term(self, node):
 
2596
        """definition list term"""
 
2597
        # Commands with optional args inside an optional arg must be put
 
2598
        # in a group, e.g. ``\item[{\hyperref[label]{text}}]``.
 
2599
        self.out.append('\\item[{')
 
2600
 
 
2601
    def depart_term(self, node):
 
2602
        # \leavevmode results in a line break if the
 
2603
        # term is followed by an item list.
 
2604
        self.out.append('}] \leavevmode ')
 
2605
 
 
2606
    def visit_tgroup(self, node):
 
2607
        #self.out.append(self.starttag(node, 'colgroup'))
 
2608
        #self.context.append('</colgroup>\n')
 
2609
        pass
 
2610
 
 
2611
    def depart_tgroup(self, node):
 
2612
        pass
 
2613
 
 
2614
    _thead_depth = 0
 
2615
    def thead_depth (self):
 
2616
        return self._thead_depth
 
2617
 
 
2618
    def visit_thead(self, node):
 
2619
        self._thead_depth += 1
 
2620
        if 1 == self.thead_depth():
 
2621
            self.out.append('{%s}\n' % self.active_table.get_colspecs())
 
2622
            self.active_table.set('preamble written',1)
 
2623
        self.out.append(self.active_table.get_caption())
 
2624
        self.out.extend(self.active_table.visit_thead())
 
2625
 
 
2626
    def depart_thead(self, node):
 
2627
        if node is not None:
 
2628
            self.out.extend(self.active_table.depart_thead())
 
2629
            if self.active_table.need_recurse():
 
2630
                node.walkabout(self)
 
2631
        self._thead_depth -= 1
 
2632
 
 
2633
    def bookmark(self, node):
 
2634
        """Return label and pdfbookmark string for titles."""
 
2635
        result = ['']
 
2636
        if self.settings.sectnum_xform: # "starred" section cmd
 
2637
            # add to the toc and pdfbookmarks
 
2638
            section_name = self.d_class.section(max(self.section_level, 1))
 
2639
            section_title = self.encode(node.astext())
 
2640
            result.append(r'\phantomsection')
 
2641
            result.append(r'\addcontentsline{toc}{%s}{%s}' %
 
2642
                          (section_name, section_title))
 
2643
        result += self.ids_to_labels(node.parent, set_anchor=False)
 
2644
        return '%\n  '.join(result) + '%\n'
 
2645
 
 
2646
    def visit_title(self, node):
 
2647
        """Append section and other titles."""
 
2648
        # Document title
 
2649
        if node.parent.tagname == 'document':
 
2650
            self.push_output_collector(self.title)
 
2651
            self.context.append('')
 
2652
            self.pdfinfo.append('  pdftitle={%s},' %
 
2653
                                self.encode(node.astext()))
 
2654
        # Topic titles (topic, admonition, sidebar)
 
2655
        elif (isinstance(node.parent, nodes.topic) or
 
2656
              isinstance(node.parent, nodes.admonition) or
 
2657
              isinstance(node.parent, nodes.sidebar)):
 
2658
            self.fallbacks['title'] = PreambleCmds.title
 
2659
            classes = ','.join(node.parent['classes'])
 
2660
            if not classes:
 
2661
                classes = node.tagname
 
2662
            self.out.append('\\DUtitle[%s]{' % classes)
 
2663
            self.context.append('}\n')
 
2664
        # Table caption
 
2665
        elif isinstance(node.parent, nodes.table):
 
2666
            self.push_output_collector(self.active_table.caption)
 
2667
            self.context.append('')
 
2668
        # Section title
 
2669
        else:
 
2670
            self.out.append('\n\n')
 
2671
            self.out.append('%' + '_' * 75)
 
2672
            self.out.append('\n\n')
 
2673
            #
 
2674
            section_name = self.d_class.section(self.section_level)
 
2675
            # number sections?
 
2676
            if (self.settings.sectnum_xform # numbering by Docutils
 
2677
                or (self.section_level > len(self.d_class.sections))):
 
2678
                section_star = '*'
 
2679
            else: # LaTeX numbered sections
 
2680
                section_star = ''
 
2681
            self.out.append(r'\%s%s{' % (section_name, section_star))
 
2682
            # System messages heading in red:
 
2683
            if ('system-messages' in node.parent['classes']):
 
2684
                self.requirements['color'] = PreambleCmds.color
 
2685
                self.out.append('\color{red}')
 
2686
            # label and ToC entry:
 
2687
            self.context.append(self.bookmark(node) + '}\n')
 
2688
            # MAYBE postfix paragraph and subparagraph with \leavemode to
 
2689
            # ensure floats stay in the section and text starts on a new line.
 
2690
 
 
2691
    def depart_title(self, node):
 
2692
        self.out.append(self.context.pop())
 
2693
        if (isinstance(node.parent, nodes.table) or
 
2694
            node.parent.tagname == 'document'):
 
2695
            self.pop_output_collector()
 
2696
 
 
2697
    def minitoc(self, node, title, depth):
 
2698
        """Generate a local table of contents with LaTeX package minitoc"""
 
2699
        section_name = self.d_class.section(self.section_level)
 
2700
        # name-prefix for current section level
 
2701
        minitoc_names = {'part': 'part', 'chapter': 'mini'}
 
2702
        if 'chapter' not in self.d_class.sections:
 
2703
            minitoc_names['section'] = 'sect'
 
2704
        try:
 
2705
            minitoc_name = minitoc_names[section_name]
 
2706
        except KeyError: # minitoc only supports part- and toplevel
 
2707
            self.warn('Skipping local ToC at %s level.\n' % section_name +
 
2708
                      '  Feature not supported with option "use-latex-toc"',
 
2709
                      base_node=node)
 
2710
            return
 
2711
        # Requirements/Setup
 
2712
        self.requirements['minitoc'] = PreambleCmds.minitoc
 
2713
        self.requirements['minitoc-'+minitoc_name] = (r'\do%stoc' %
 
2714
                                                      minitoc_name)
 
2715
        # depth: (Docutils defaults to unlimited depth)
 
2716
        maxdepth = len(self.d_class.sections)
 
2717
        self.requirements['minitoc-%s-depth' % minitoc_name] = (
 
2718
            r'\mtcsetdepth{%stoc}{%d}' % (minitoc_name, maxdepth))
 
2719
        # Process 'depth' argument (!Docutils stores a relative depth while
 
2720
        # minitoc  expects an absolute depth!):
 
2721
        offset = {'sect': 1, 'mini': 0, 'part': 0}
 
2722
        if 'chapter' in self.d_class.sections:
 
2723
            offset['part'] = -1
 
2724
        if depth:
 
2725
            self.out.append('\\setcounter{%stocdepth}{%d}' %
 
2726
                             (minitoc_name, depth + offset[minitoc_name]))
 
2727
        # title:
 
2728
        self.out.append('\\mtcsettitle{%stoc}{%s}\n' % (minitoc_name, title))
 
2729
        # the toc-generating command:
 
2730
        self.out.append('\\%stoc\n' % minitoc_name)
 
2731
 
 
2732
    def visit_topic(self, node):
 
2733
        # Topic nodes can be generic topic, abstract, dedication, or ToC.
 
2734
        # table of contents:
 
2735
        if 'contents' in node['classes']:
 
2736
            self.out.append('\n')
 
2737
            self.out += self.ids_to_labels(node)
 
2738
            # add contents to PDF bookmarks sidebar
 
2739
            if isinstance(node.next_node(), nodes.title):
 
2740
                self.out.append('\n\\pdfbookmark[%d]{%s}{%s}\n' %
 
2741
                                (self.section_level+1,
 
2742
                                 node.next_node().astext(),
 
2743
                                 node.get('ids', ['contents'])[0]
 
2744
                                ))
 
2745
            if self.use_latex_toc:
 
2746
                title = ''
 
2747
                if isinstance(node.next_node(), nodes.title):
 
2748
                    title = self.encode(node.pop(0).astext())
 
2749
                depth = node.get('depth', 0)
 
2750
                if 'local' in node['classes']:
 
2751
                    self.minitoc(title, node, depth)
 
2752
                    self.context.append('')
 
2753
                    return
 
2754
                if depth:
 
2755
                    self.out.append('\\setcounter{tocdepth}{%d}\n' % depth)
 
2756
                if title != 'Contents':
 
2757
                    self.out.append('\\renewcommand{\\contentsname}{%s}\n' %
 
2758
                                    title)
 
2759
                self.out.append('\\tableofcontents\n\n')
 
2760
                self.has_latex_toc = True
 
2761
            else: # Docutils generated contents list
 
2762
                # set flag for visit_bullet_list() and visit_title()
 
2763
                self.is_toc_list = True
 
2764
            self.context.append('')
 
2765
        elif ('abstract' in node['classes'] and
 
2766
              self.settings.use_latex_abstract):
 
2767
            self.push_output_collector(self.abstract)
 
2768
            self.out.append('\\begin{abstract}')
 
2769
            self.context.append('\\end{abstract}\n')
 
2770
            if isinstance(node.next_node(), nodes.title):
 
2771
                node.pop(0) # LaTeX provides its own title
 
2772
        else:
 
2773
            self.fallbacks['topic'] = PreambleCmds.topic
 
2774
            # special topics:
 
2775
            if 'abstract' in node['classes']:
 
2776
                self.fallbacks['abstract'] = PreambleCmds.abstract
 
2777
                self.push_output_collector(self.abstract)
 
2778
            if 'dedication' in node['classes']:
 
2779
                self.fallbacks['dedication'] = PreambleCmds.dedication
 
2780
                self.push_output_collector(self.dedication)
 
2781
            self.out.append('\n\\DUtopic[%s]{\n' % ','.join(node['classes']))
 
2782
            self.context.append('}\n')
 
2783
 
 
2784
    def depart_topic(self, node):
 
2785
        self.out.append(self.context.pop())
 
2786
        self.is_toc_list = False
 
2787
        if ('abstract' in node['classes'] or
 
2788
            'dedication' in node['classes']):
 
2789
            self.pop_output_collector()
 
2790
 
 
2791
    def visit_inline(self, node): # <span>, i.e. custom roles
 
2792
        # insert fallback definition
 
2793
        self.fallbacks['inline'] = PreambleCmds.inline
 
2794
        self.out += [r'\DUrole{%s}{' % cls for cls in node['classes']]
 
2795
        self.context.append('}' * (len(node['classes'])))
 
2796
 
 
2797
    def depart_inline(self, node):
 
2798
        self.out.append(self.context.pop())
 
2799
 
 
2800
    def visit_rubric(self, node):
 
2801
        self.fallbacks['rubric'] = PreambleCmds.rubric
 
2802
        self.out.append('\n\\DUrubric{')
 
2803
        self.context.append('}\n')
 
2804
 
 
2805
    def depart_rubric(self, node):
 
2806
        self.out.append(self.context.pop())
 
2807
 
 
2808
    def visit_transition(self, node):
 
2809
        self.fallbacks['transition'] = PreambleCmds.transition
 
2810
        self.out.append('\n\n')
 
2811
        self.out.append('%' + '_' * 75 + '\n')
 
2812
        self.out.append(r'\DUtransition')
 
2813
        self.out.append('\n\n')
 
2814
 
 
2815
    def depart_transition(self, node):
 
2816
        pass
 
2817
 
 
2818
    def visit_version(self, node):
 
2819
        self.visit_docinfo_item(node, 'version')
 
2820
 
 
2821
    def depart_version(self, node):
 
2822
        self.depart_docinfo_item(node)
 
2823
 
 
2824
    def unimplemented_visit(self, node):
 
2825
        raise NotImplementedError('visiting unimplemented node type: %s' %
 
2826
                                  node.__class__.__name__)
 
2827
 
 
2828
#    def unknown_visit(self, node):
 
2829
#    def default_visit(self, node):
 
2830
 
 
2831
# vim: set ts=4 et ai :