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