Fix: bug# 1612270 double qoutes in italian literal.
[docutils.git] / docutils / writers / latex2e / __init__.py
blob051402817f2de19229f32a433e73eb82fb095d6a
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 elif re.search('^it',self.language):
244 self.quotes = ("``", "''")
245 self.double_quote_replacment = r'{\char`\"}'
246 else:
247 self.quotes = ("``", "''")
248 self.quote_index = 0
250 def next_quote(self):
251 q = self.quotes[self.quote_index]
252 self.quote_index = (self.quote_index+1)%2
253 return q
255 def quote_quotes(self,text):
256 t = None
257 for part in text.split('"'):
258 if t == None:
259 t = part
260 else:
261 t += self.next_quote() + part
262 return t
264 def double_quotes_in_tt (self,text):
265 if not self.double_quote_replacment:
266 return text
267 return text.replace('"', self.double_quote_replacment)
269 def get_language(self):
270 if self._ISO639_TO_BABEL.has_key(self.language):
271 return self._ISO639_TO_BABEL[self.language]
272 else:
273 # support dialects.
274 l = self.language.split("_")[0]
275 if self._ISO639_TO_BABEL.has_key(l):
276 return self._ISO639_TO_BABEL[l]
277 return None
280 latex_headings = {
281 'optionlist_environment' : [
282 '\\newcommand{\\optionlistlabel}[1]{\\bf #1 \\hfill}\n'
283 '\\newenvironment{optionlist}[1]\n'
284 '{\\begin{list}{}\n'
285 ' {\\setlength{\\labelwidth}{#1}\n'
286 ' \\setlength{\\rightmargin}{1cm}\n'
287 ' \\setlength{\\leftmargin}{\\rightmargin}\n'
288 ' \\addtolength{\\leftmargin}{\\labelwidth}\n'
289 ' \\addtolength{\\leftmargin}{\\labelsep}\n'
290 ' \\renewcommand{\\makelabel}{\\optionlistlabel}}\n'
291 '}{\\end{list}}\n',
293 'lineblock_environment' : [
294 '\\newlength{\\lineblockindentation}\n'
295 '\\setlength{\\lineblockindentation}{2.5em}\n'
296 '\\newenvironment{lineblock}[1]\n'
297 '{\\begin{list}{}\n'
298 ' {\\setlength{\\partopsep}{\\parskip}\n'
299 ' \\addtolength{\\partopsep}{\\baselineskip}\n'
300 ' \\topsep0pt\\itemsep0.15\\baselineskip\\parsep0pt\n'
301 ' \\leftmargin#1}\n'
302 ' \\raggedright}\n'
303 '{\\end{list}}\n'
305 'footnote_floats' : [
306 '% begin: floats for footnotes tweaking.\n',
307 '\\setlength{\\floatsep}{0.5em}\n',
308 '\\setlength{\\textfloatsep}{\\fill}\n',
309 '\\addtolength{\\textfloatsep}{3em}\n',
310 '\\renewcommand{\\textfraction}{0.5}\n',
311 '\\renewcommand{\\topfraction}{0.5}\n',
312 '\\renewcommand{\\bottomfraction}{0.5}\n',
313 '\\setcounter{totalnumber}{50}\n',
314 '\\setcounter{topnumber}{50}\n',
315 '\\setcounter{bottomnumber}{50}\n',
316 '% end floats for footnotes\n',
318 'some_commands' : [
319 '% some commands, that could be overwritten in the style file.\n'
320 '\\newcommand{\\rubric}[1]'
321 '{\\subsection*{~\\hfill {\\it #1} \\hfill ~}}\n'
322 '\\newcommand{\\titlereference}[1]{\\textsl{#1}}\n'
323 '% end of "some commands"\n',
327 class DocumentClass:
328 """Details of a LaTeX document class."""
330 # BUG: LaTeX has no deeper sections (actually paragrah is no
331 # section either).
332 # BUG: No support for unknown document classes. Make 'article'
333 # default?
334 _class_sections = {
335 'book': ( 'chapter', 'section', 'subsection', 'subsubsection' ),
336 'scrbook': ( 'chapter', 'section', 'subsection', 'subsubsection' ),
337 'report': ( 'chapter', 'section', 'subsection', 'subsubsection' ),
338 'scrreprt': ( 'chapter', 'section', 'subsection', 'subsubsection' ),
339 'article': ( 'section', 'subsection', 'subsubsection' ),
340 'scrartcl': ( 'section', 'subsection', 'subsubsection' ),
342 _deepest_section = 'subsubsection'
344 def __init__(self, document_class):
345 self.document_class = document_class
347 def section(self, level):
348 """ Return the section name at the given level for the specific
349 document class.
351 Level is 1,2,3..., as level 0 is the title."""
353 sections = self._class_sections[self.document_class]
354 if level <= len(sections):
355 return sections[level-1]
356 else:
357 return self._deepest_section
359 class Table:
360 """ Manage a table while traversing.
361 Maybe change to a mixin defining the visit/departs, but then
362 class Table internal variables are in the Translator.
364 Table style might be
366 * standard: horizontal and vertical lines
367 * booktabs (requires booktabs latex package): only horizontal lines
368 * nolines, borderless : no lines
370 def __init__(self,latex_type,table_style):
371 self._latex_type = latex_type
372 self._table_style = table_style
373 self._open = 0
374 # miscellaneous attributes
375 self._attrs = {}
376 self._col_width = []
377 self._rowspan = []
379 def open(self):
380 self._open = 1
381 self._col_specs = []
382 self.caption = None
383 self._attrs = {}
384 self._in_head = 0 # maybe context with search
385 def close(self):
386 self._open = 0
387 self._col_specs = None
388 self.caption = None
389 self._attrs = {}
390 def is_open(self):
391 return self._open
392 def set_table_style(self, table_style):
393 if not table_style in ('standard','booktabs','borderless','nolines'):
394 return
395 self._table_style = table_style
397 def used_packages(self):
398 if self._table_style == 'booktabs':
399 return '\\usepackage{booktabs}\n'
400 return ''
401 def get_latex_type(self):
402 return self._latex_type
404 def set(self,attr,value):
405 self._attrs[attr] = value
406 def get(self,attr):
407 if self._attrs.has_key(attr):
408 return self._attrs[attr]
409 return None
410 def get_vertical_bar(self):
411 if self._table_style == 'standard':
412 return '|'
413 return ''
414 # horizontal lines are drawn below a row, because we.
415 def get_opening(self):
416 return '\\begin{%s}[c]' % self._latex_type
417 def get_closing(self):
418 line = ""
419 if self._table_style == 'booktabs':
420 line = '\\bottomrule\n'
421 elif self._table_style == 'standard':
422 lines = '\\hline\n'
423 return '%s\\end{%s}' % (line,self._latex_type)
425 def visit_colspec(self,node):
426 self._col_specs.append(node)
428 def get_colspecs(self):
430 Return column specification for longtable.
432 Assumes reST line length being 80 characters.
433 Table width is hairy.
435 === ===
436 ABC DEF
437 === ===
439 usually gets to narrow, therefore we add 1 (fiddlefactor).
441 width = 80
443 total_width = 0.0
444 # first see if we get too wide.
445 for node in self._col_specs:
446 colwidth = float(node['colwidth']+1) / width
447 total_width += colwidth
448 self._col_width = []
449 self._rowspan = []
450 # donot make it full linewidth
451 factor = 0.93
452 if total_width > 1.0:
453 factor /= total_width
454 bar = self.get_vertical_bar()
455 latex_table_spec = ""
456 for node in self._col_specs:
457 colwidth = factor * float(node['colwidth']+1) / width
458 self._col_width.append(colwidth+0.005)
459 self._rowspan.append(0)
460 latex_table_spec += "%sp{%.2f\\locallinewidth}" % (bar,colwidth+0.005)
461 return latex_table_spec+bar
463 def get_column_width(self):
464 """ return columnwidth for current cell (not multicell)
466 return "%.2f\\locallinewidth" % self._col_width[self._cell_in_row-1]
468 def visit_thead(self):
469 self._in_thead = 1
470 if self._table_style == 'standard':
471 return ['\\hline\n']
472 elif self._table_style == 'booktabs':
473 return ['\\toprule\n']
474 return []
475 def depart_thead(self):
476 a = []
477 #if self._table_style == 'standard':
478 # a.append('\\hline\n')
479 if self._table_style == 'booktabs':
480 a.append('\\midrule\n')
481 a.append('\\endhead\n')
482 # for longtable one could add firsthead, foot and lastfoot
483 self._in_thead = 0
484 return a
485 def visit_row(self):
486 self._cell_in_row = 0
487 def depart_row(self):
488 res = [' \\\\\n']
489 self._cell_in_row = None # remove cell counter
490 for i in range(len(self._rowspan)):
491 if (self._rowspan[i]>0):
492 self._rowspan[i] -= 1
494 if self._table_style == 'standard':
495 rowspans = []
496 for i in range(len(self._rowspan)):
497 if (self._rowspan[i]<=0):
498 rowspans.append(i+1)
499 if len(rowspans)==len(self._rowspan):
500 res.append('\\hline\n')
501 else:
502 cline = ''
503 rowspans.reverse()
504 # TODO merge clines
505 while 1:
506 try:
507 c_start = rowspans.pop()
508 except:
509 break
510 cline += '\\cline{%d-%d}\n' % (c_start,c_start)
511 res.append(cline)
512 return res
514 def set_rowspan(self,cell,value):
515 try:
516 self._rowspan[cell] = value
517 except:
518 pass
519 def get_rowspan(self,cell):
520 try:
521 return self._rowspan[cell]
522 except:
523 return 0
524 def get_entry_number(self):
525 return self._cell_in_row
526 def visit_entry(self):
527 self._cell_in_row += 1
530 class LaTeXTranslator(nodes.NodeVisitor):
532 # When options are given to the documentclass, latex will pass them
533 # to other packages, as done with babel.
534 # Dummy settings might be taken from document settings
536 latex_head = '\\documentclass[%s]{%s}\n'
537 encoding = '\\usepackage[%s]{inputenc}\n'
538 linking = '\\usepackage[colorlinks=%s,linkcolor=%s,urlcolor=%s]{hyperref}\n'
539 stylesheet = '\\input{%s}\n'
540 # add a generated on day , machine by user using docutils version.
541 generator = '%% generator Docutils: http://docutils.sourceforge.net/\n'
543 # use latex tableofcontents or let docutils do it.
544 use_latex_toc = 0
546 # TODO: use mixins for different implementations.
547 # list environment for option-list. else tabularx
548 use_optionlist_for_option_list = 1
549 # list environment for docinfo. else tabularx
550 use_optionlist_for_docinfo = 0 # NOT YET IN USE
552 # Use compound enumerations (1.A.1.)
553 compound_enumerators = 0
555 # If using compound enumerations, include section information.
556 section_prefix_for_enumerators = 0
558 # This is the character that separates the section ("." subsection ...)
559 # prefix from the regular list enumerator.
560 section_enumerator_separator = '-'
562 # default link color
563 hyperlink_color = "blue"
565 def __init__(self, document):
566 nodes.NodeVisitor.__init__(self, document)
567 self.settings = settings = document.settings
568 self.latex_encoding = self.to_latex_encoding(settings.output_encoding)
569 self.use_latex_toc = settings.use_latex_toc
570 self.use_latex_docinfo = settings.use_latex_docinfo
571 self.use_latex_footnotes = settings.use_latex_footnotes
572 self._use_latex_citations = settings.use_latex_citations
573 self.hyperlink_color = settings.hyperlink_color
574 self.compound_enumerators = settings.compound_enumerators
575 self.font_encoding = settings.font_encoding
576 self.section_prefix_for_enumerators = (
577 settings.section_prefix_for_enumerators)
578 self.section_enumerator_separator = (
579 settings.section_enumerator_separator.replace('_', '\\_'))
580 if self.hyperlink_color == '0':
581 self.hyperlink_color = 'black'
582 self.colorlinks = 'false'
583 else:
584 self.colorlinks = 'true'
586 # language: labels, bibliographic_fields, and author_separators.
587 # to allow writing labes for specific languages.
588 self.language = languages.get_language(settings.language_code)
589 self.babel = Babel(settings.language_code)
590 self.author_separator = self.language.author_separators[0]
591 self.d_options = self.settings.documentoptions
592 if self.babel.get_language():
593 self.d_options += ',%s' % \
594 self.babel.get_language()
596 self.d_class = DocumentClass(settings.documentclass)
597 # object for a table while proccessing.
598 self.active_table = Table('longtable',settings.table_style)
600 # HACK. Should have more sophisticated typearea handling.
601 if settings.documentclass.find('scr') == -1:
602 self.typearea = '\\usepackage[DIV12]{typearea}\n'
603 else:
604 if self.d_options.find('DIV') == -1 and self.d_options.find('BCOR') == -1:
605 self.typearea = '\\typearea{12}\n'
606 else:
607 self.typearea = ''
609 if self.font_encoding == 'OT1':
610 fontenc_header = ''
611 elif self.font_encoding == '':
612 fontenc_header = '\\usepackage{ae}\n\\usepackage{aeguill}\n'
613 else:
614 fontenc_header = '\\usepackage[%s]{fontenc}\n' % (self.font_encoding,)
615 input_encoding = self.encoding % self.latex_encoding
616 if self.settings.graphicx_option == '':
617 self.graphicx_package = '\\usepackage{graphicx}\n'
618 elif self.settings.graphicx_option.lower() == 'auto':
619 self.graphicx_package = '\n'.join(
620 ('%Check if we are compiling under latex or pdflatex',
621 '\\ifx\\pdftexversion\\undefined',
622 ' \\usepackage{graphicx}',
623 '\\else',
624 ' \\usepackage[pdftex]{graphicx}',
625 '\\fi\n'))
626 else:
627 self.graphicx_package = (
628 '\\usepackage[%s]{graphicx}\n' % self.settings.graphicx_option)
630 self.head_prefix = [
631 self.latex_head % (self.d_options,self.settings.documentclass),
632 '\\usepackage{babel}\n', # language is in documents settings.
633 fontenc_header,
634 '\\usepackage{shortvrb}\n', # allows verb in footnotes.
635 input_encoding,
636 # * tabularx: for docinfo, automatic width of columns, always on one page.
637 '\\usepackage{tabularx}\n',
638 '\\usepackage{longtable}\n',
639 self.active_table.used_packages(),
640 # possible other packages.
641 # * fancyhdr
642 # * ltxtable is a combination of tabularx and longtable (pagebreaks).
643 # but ??
645 # extra space between text in tables and the line above them
646 '\\setlength{\\extrarowheight}{2pt}\n',
647 '\\usepackage{amsmath}\n', # what fore amsmath.
648 self.graphicx_package,
649 '\\usepackage{color}\n',
650 '\\usepackage{multirow}\n',
651 '\\usepackage{ifthen}\n', # before hyperref!
652 self.linking % (self.colorlinks, self.hyperlink_color, self.hyperlink_color),
653 self.typearea,
654 self.generator,
655 # latex lengths
656 '\\newlength{\\admonitionwidth}\n',
657 '\\setlength{\\admonitionwidth}{0.9\\textwidth}\n'
658 # width for docinfo tablewidth
659 '\\newlength{\\docinfowidth}\n',
660 '\\setlength{\\docinfowidth}{0.9\\textwidth}\n'
661 # linewidth of current environment, so tables are not wider
662 # than the sidebar: using locallinewidth seems to defer evaluation
663 # of linewidth, this is fixing it.
664 '\\newlength{\\locallinewidth}\n',
665 # will be set later.
667 self.head_prefix.extend( latex_headings['optionlist_environment'] )
668 self.head_prefix.extend( latex_headings['lineblock_environment'] )
669 self.head_prefix.extend( latex_headings['footnote_floats'] )
670 self.head_prefix.extend( latex_headings['some_commands'] )
671 ## stylesheet is last: so it might be possible to overwrite defaults.
672 stylesheet = utils.get_stylesheet_reference(settings)
673 if stylesheet:
674 settings.record_dependencies.add(stylesheet)
675 self.head_prefix.append(self.stylesheet % (stylesheet))
677 if self.linking: # and maybe check for pdf
678 self.pdfinfo = [ ]
679 self.pdfauthor = None
680 # pdftitle, pdfsubject, pdfauthor, pdfkeywords, pdfcreator, pdfproducer
681 else:
682 self.pdfinfo = None
683 # NOTE: Latex wants a date and an author, rst puts this into
684 # docinfo, so normally we donot want latex author/date handling.
685 # latex article has its own handling of date and author, deactivate.
686 # So we always emit \title{...} \author{...} \date{...}, even if the
687 # "..." are empty strings.
688 self.head = [ ]
689 # separate title, so we can appen subtitle.
690 self.title = ''
691 # if use_latex_docinfo: collects lists of author/organization/contact/address lines
692 self.author_stack = []
693 self.date = ''
695 self.body_prefix = ['\\raggedbottom\n']
696 self.body = []
697 self.body_suffix = ['\n']
698 self.section_level = 0
699 self.context = []
700 self.topic_classes = []
701 # column specification for tables
702 self.table_caption = None
704 # Flags to encode
705 # ---------------
706 # verbatim: to tell encode not to encode.
707 self.verbatim = 0
708 # insert_newline: to tell encode to replace blanks by "~".
709 self.insert_none_breaking_blanks = 0
710 # insert_newline: to tell encode to add latex newline.
711 self.insert_newline = 0
712 # mbox_newline: to tell encode to add mbox and newline.
713 self.mbox_newline = 0
714 # inside citation reference labels underscores dont need to be escaped.
715 self.inside_citation_reference_label = 0
717 # Stack of section counters so that we don't have to use_latex_toc.
718 # This will grow and shrink as processing occurs.
719 # Initialized for potential first-level sections.
720 self._section_number = [0]
722 # The current stack of enumerations so that we can expand
723 # them into a compound enumeration.
724 self._enumeration_counters = []
726 # The maximum number of enumeration counters we've used.
727 # If we go beyond this number, we need to create a new
728 # counter; otherwise, just reuse an old one.
729 self._max_enumeration_counters = 0
731 self._bibitems = []
733 # docinfo.
734 self.docinfo = None
735 # inside literal block: no quote mangling.
736 self.literal_block = 0
737 self.literal_block_stack = []
738 self.literal = 0
739 # true when encoding in math mode
740 self.mathmode = 0
742 def to_latex_encoding(self,docutils_encoding):
744 Translate docutils encoding name into latex's.
746 Default fallback method is remove "-" and "_" chars from docutils_encoding.
749 tr = { "iso-8859-1": "latin1", # west european
750 "iso-8859-2": "latin2", # east european
751 "iso-8859-3": "latin3", # esperanto, maltese
752 "iso-8859-4": "latin4", # north european,scandinavian, baltic
753 "iso-8859-5": "iso88595", # cyrillic (ISO)
754 "iso-8859-9": "latin5", # turkish
755 "iso-8859-15": "latin9", # latin9, update to latin1.
756 "mac_cyrillic": "maccyr", # cyrillic (on Mac)
757 "windows-1251": "cp1251", # cyrillic (on Windows)
758 "koi8-r": "koi8-r", # cyrillic (Russian)
759 "koi8-u": "koi8-u", # cyrillic (Ukrainian)
760 "windows-1250": "cp1250", #
761 "windows-1252": "cp1252", #
762 "us-ascii": "ascii", # ASCII (US)
763 # unmatched encodings
764 #"": "applemac",
765 #"": "ansinew", # windows 3.1 ansi
766 #"": "ascii", # ASCII encoding for the range 32--127.
767 #"": "cp437", # dos latine us
768 #"": "cp850", # dos latin 1
769 #"": "cp852", # dos latin 2
770 #"": "decmulti",
771 #"": "latin10",
772 #"iso-8859-6": "" # arabic
773 #"iso-8859-7": "" # greek
774 #"iso-8859-8": "" # hebrew
775 #"iso-8859-10": "" # latin6, more complete iso-8859-4
777 if tr.has_key(docutils_encoding.lower()):
778 return tr[docutils_encoding.lower()]
779 return docutils_encoding.replace("_", "").replace("-", "").lower()
781 def language_label(self, docutil_label):
782 return self.language.labels[docutil_label]
784 latex_equivalents = {
785 u'\u00A0' : '~',
786 u'\u2013' : '{--}',
787 u'\u2014' : '{---}',
788 u'\u2018' : '`',
789 u'\u2019' : '\'',
790 u'\u201A' : ',',
791 u'\u201C' : '``',
792 u'\u201D' : '\'\'',
793 u'\u201E' : ',,',
794 u'\u2020' : '{\\dag}',
795 u'\u2021' : '{\\ddag}',
796 u'\u2026' : '{\\dots}',
797 u'\u2122' : '{\\texttrademark}',
798 u'\u21d4' : '{$\\Leftrightarrow$}',
801 def unicode_to_latex(self,text):
802 # see LaTeX codec
803 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252124
804 # Only some special chracters are translated, for documents with many
805 # utf-8 chars one should use the LaTeX unicode package.
806 for uchar in self.latex_equivalents.keys():
807 text = text.replace(uchar,self.latex_equivalents[uchar])
808 return text
810 def encode(self, text):
812 Encode special characters (``# $ % & ~ _ ^ \ { }``) in `text` & return
814 # Escaping with a backslash does not help with backslashes, ~ and ^.
816 # < > are only available in math-mode or tt font. (really ?)
817 # $ starts math- mode.
818 # AND quotes
819 if self.verbatim:
820 return text
821 # compile the regexps once. do it here so one can see them.
823 # first the braces.
824 if not self.__dict__.has_key('encode_re_braces'):
825 self.encode_re_braces = re.compile(r'([{}])')
826 text = self.encode_re_braces.sub(r'{\\\1}',text)
827 if not self.__dict__.has_key('encode_re_bslash'):
828 # find backslash: except in the form '{\{}' or '{\}}'.
829 self.encode_re_bslash = re.compile(r'(?<!{)(\\)(?![{}]})')
830 # then the backslash: except in the form from line above:
831 # either '{\{}' or '{\}}'.
832 text = self.encode_re_bslash.sub(r'{\\textbackslash}', text)
834 # then dollar
835 text = text.replace("$", '{\\$}')
836 if not ( self.literal_block or self.literal or self.mathmode ):
837 # the vertical bar: in mathmode |,\vert or \mid
838 # in textmode \textbar
839 text = text.replace("|", '{\\textbar}')
840 text = text.replace("<", '{\\textless}')
841 text = text.replace(">", '{\\textgreater}')
842 # then
843 text = text.replace("&", '{\\&}')
844 # the ^:
845 # * verb|^| does not work in mbox.
846 # * mathmode has wedge. hat{~} would also work.
847 # text = text.replace("^", '{\\ensuremath{^\\wedge}}')
848 text = text.replace("^", '{\\textasciicircum}')
849 text = text.replace("%", '{\\%}')
850 text = text.replace("#", '{\\#}')
851 text = text.replace("~", '{\\textasciitilde}')
852 # Separate compound characters, e.g. "--" to "-{}-". (The
853 # actual separation is done later; see below.)
854 separate_chars = '-'
855 if self.literal_block or self.literal:
856 # In monospace-font, we also separate ",,", "``" and "''"
857 # and some other characters which can't occur in
858 # non-literal text.
859 separate_chars += ',`\'"<>'
860 # pdflatex does not produce doublequotes for ngerman.
861 text = self.babel.double_quotes_in_tt(text)
862 if self.font_encoding == 'OT1':
863 # We're using OT1 font-encoding and have to replace
864 # underscore by underlined blank, because this has
865 # correct width.
866 text = text.replace('_', '{\\underline{ }}')
867 # And the tt-backslash doesn't work in OT1, so we use
868 # a mirrored slash.
869 text = text.replace('\\textbackslash', '\\reflectbox{/}')
870 else:
871 text = text.replace('_', '{\\_}')
872 else:
873 text = self.babel.quote_quotes(text)
874 if not self.inside_citation_reference_label:
875 text = text.replace("_", '{\\_}')
876 for char in separate_chars * 2:
877 # Do it twice ("* 2") becaues otherwise we would replace
878 # "---" by "-{}--".
879 text = text.replace(char + char, char + '{}' + char)
880 if self.insert_newline or self.literal_block:
881 # Insert a blank before the newline, to avoid
882 # ! LaTeX Error: There's no line here to end.
883 text = text.replace("\n", '~\\\\\n')
884 elif self.mbox_newline:
885 if self.literal_block:
886 closings = "}" * len(self.literal_block_stack)
887 openings = "".join(self.literal_block_stack)
888 else:
889 closings = ""
890 openings = ""
891 text = text.replace("\n", "%s}\\\\\n\\mbox{%s" % (closings,openings))
892 text = text.replace('[', '{[}').replace(']', '{]}')
893 if self.insert_none_breaking_blanks:
894 text = text.replace(' ', '~')
895 if self.latex_encoding != 'utf8':
896 text = self.unicode_to_latex(text)
897 return text
899 def attval(self, text,
900 whitespace=re.compile('[\n\r\t\v\f]')):
901 """Cleanse, encode, and return attribute value text."""
902 return self.encode(whitespace.sub(' ', text))
904 def astext(self):
905 if self.pdfinfo is not None:
906 if self.pdfauthor:
907 self.pdfinfo.append('pdfauthor={%s}' % self.pdfauthor)
908 if self.pdfinfo:
909 pdfinfo = '\\hypersetup{\n' + ',\n'.join(self.pdfinfo) + '\n}\n'
910 else:
911 pdfinfo = ''
912 head = '\\title{%s}\n\\author{%s}\n\\date{%s}\n' % \
913 (self.title,
914 ' \\and\n'.join(['~\\\\\n'.join(author_lines)
915 for author_lines in self.author_stack]),
916 self.date)
917 return ''.join(self.head_prefix + [head] + self.head + [pdfinfo]
918 + self.body_prefix + self.body + self.body_suffix)
920 def visit_Text(self, node):
921 self.body.append(self.encode(node.astext()))
923 def depart_Text(self, node):
924 pass
926 def visit_address(self, node):
927 self.visit_docinfo_item(node, 'address')
929 def depart_address(self, node):
930 self.depart_docinfo_item(node)
932 def visit_admonition(self, node, name=''):
933 self.body.append('\\begin{center}\\begin{sffamily}\n')
934 self.body.append('\\fbox{\\parbox{\\admonitionwidth}{\n')
935 if name:
936 self.body.append('\\textbf{\\large '+ self.language.labels[name] + '}\n');
937 self.body.append('\\vspace{2mm}\n')
940 def depart_admonition(self, node=None):
941 self.body.append('}}\n') # end parbox fbox
942 self.body.append('\\end{sffamily}\n\\end{center}\n');
944 def visit_attention(self, node):
945 self.visit_admonition(node, 'attention')
947 def depart_attention(self, node):
948 self.depart_admonition()
950 def visit_author(self, node):
951 self.visit_docinfo_item(node, 'author')
953 def depart_author(self, node):
954 self.depart_docinfo_item(node)
956 def visit_authors(self, node):
957 # not used: visit_author is called anyway for each author.
958 pass
960 def depart_authors(self, node):
961 pass
963 def visit_block_quote(self, node):
964 self.body.append( '\\begin{quote}\n')
966 def depart_block_quote(self, node):
967 self.body.append( '\\end{quote}\n')
969 def visit_bullet_list(self, node):
970 if 'contents' in self.topic_classes:
971 if self.use_latex_toc:
972 raise nodes.SkipNode
973 self.body.append( '\\begin{list}{}{}\n' )
974 else:
975 self.body.append( '\\begin{itemize}\n' )
977 def depart_bullet_list(self, node):
978 if 'contents' in self.topic_classes:
979 self.body.append( '\\end{list}\n' )
980 else:
981 self.body.append( '\\end{itemize}\n' )
983 # Imperfect superscript/subscript handling: mathmode italicizes
984 # all letters by default.
985 def visit_superscript(self, node):
986 self.body.append('$^{')
987 self.mathmode = 1
989 def depart_superscript(self, node):
990 self.body.append('}$')
991 self.mathmode = 0
993 def visit_subscript(self, node):
994 self.body.append('$_{')
995 self.mathmode = 1
997 def depart_subscript(self, node):
998 self.body.append('}$')
999 self.mathmode = 0
1001 def visit_caption(self, node):
1002 self.body.append( '\\caption{' )
1004 def depart_caption(self, node):
1005 self.body.append('}')
1007 def visit_caution(self, node):
1008 self.visit_admonition(node, 'caution')
1010 def depart_caution(self, node):
1011 self.depart_admonition()
1013 def visit_title_reference(self, node):
1014 self.body.append( '\\titlereference{' )
1016 def depart_title_reference(self, node):
1017 self.body.append( '}' )
1019 def visit_citation(self, node):
1020 # TODO maybe use cite bibitems
1021 if self._use_latex_citations:
1022 self.context.append(len(self.body))
1023 else:
1024 self.body.append('\\begin{figure}[b]')
1025 for id in node['ids']:
1026 self.body.append('\\hypertarget{%s}' % id)
1028 def depart_citation(self, node):
1029 if self._use_latex_citations:
1030 size = self.context.pop()
1031 label = self.body[size]
1032 text = ''.join(self.body[size+1:])
1033 del self.body[size:]
1034 self._bibitems.append([label, text])
1035 else:
1036 self.body.append('\\end{figure}\n')
1038 def visit_citation_reference(self, node):
1039 if self._use_latex_citations:
1040 self.body.append('\\cite{')
1041 self.inside_citation_reference_label = 1
1042 else:
1043 href = ''
1044 if node.has_key('refid'):
1045 href = node['refid']
1046 elif node.has_key('refname'):
1047 href = self.document.nameids[node['refname']]
1048 self.body.append('[\\hyperlink{%s}{' % href)
1050 def depart_citation_reference(self, node):
1051 if self._use_latex_citations:
1052 self.body.append('}')
1053 self.inside_citation_reference_label = 0
1054 else:
1055 self.body.append('}]')
1057 def visit_classifier(self, node):
1058 self.body.append( '(\\textbf{' )
1060 def depart_classifier(self, node):
1061 self.body.append( '})\n' )
1063 def visit_colspec(self, node):
1064 self.active_table.visit_colspec(node)
1066 def depart_colspec(self, node):
1067 pass
1069 def visit_comment(self, node):
1070 # Escape end of line by a new comment start in comment text.
1071 self.body.append('%% %s \n' % node.astext().replace('\n', '\n% '))
1072 raise nodes.SkipNode
1074 def visit_compound(self, node):
1075 pass
1077 def depart_compound(self, node):
1078 pass
1080 def visit_contact(self, node):
1081 self.visit_docinfo_item(node, 'contact')
1083 def depart_contact(self, node):
1084 self.depart_docinfo_item(node)
1086 def visit_container(self, node):
1087 pass
1089 def depart_container(self, node):
1090 pass
1092 def visit_copyright(self, node):
1093 self.visit_docinfo_item(node, 'copyright')
1095 def depart_copyright(self, node):
1096 self.depart_docinfo_item(node)
1098 def visit_danger(self, node):
1099 self.visit_admonition(node, 'danger')
1101 def depart_danger(self, node):
1102 self.depart_admonition()
1104 def visit_date(self, node):
1105 self.visit_docinfo_item(node, 'date')
1107 def depart_date(self, node):
1108 self.depart_docinfo_item(node)
1110 def visit_decoration(self, node):
1111 pass
1113 def depart_decoration(self, node):
1114 pass
1116 def visit_definition(self, node):
1117 self.body.append('%[visit_definition]\n')
1119 def depart_definition(self, node):
1120 self.body.append('\n')
1121 self.body.append('%[depart_definition]\n')
1123 def visit_definition_list(self, node):
1124 self.body.append( '\\begin{description}\n' )
1126 def depart_definition_list(self, node):
1127 self.body.append( '\\end{description}\n' )
1129 def visit_definition_list_item(self, node):
1130 self.body.append('%[visit_definition_list_item]\n')
1132 def depart_definition_list_item(self, node):
1133 self.body.append('%[depart_definition_list_item]\n')
1135 def visit_description(self, node):
1136 if self.use_optionlist_for_option_list:
1137 self.body.append( ' ' )
1138 else:
1139 self.body.append( ' & ' )
1141 def depart_description(self, node):
1142 pass
1144 def visit_docinfo(self, node):
1145 self.docinfo = []
1146 self.docinfo.append('%' + '_'*75 + '\n')
1147 self.docinfo.append('\\begin{center}\n')
1148 self.docinfo.append('\\begin{tabularx}{\\docinfowidth}{lX}\n')
1150 def depart_docinfo(self, node):
1151 self.docinfo.append('\\end{tabularx}\n')
1152 self.docinfo.append('\\end{center}\n')
1153 self.body = self.docinfo + self.body
1154 # clear docinfo, so field names are no longer appended.
1155 self.docinfo = None
1157 def visit_docinfo_item(self, node, name):
1158 if name == 'author':
1159 if not self.pdfinfo == None:
1160 if not self.pdfauthor:
1161 self.pdfauthor = self.attval(node.astext())
1162 else:
1163 self.pdfauthor += self.author_separator + self.attval(node.astext())
1164 if self.use_latex_docinfo:
1165 if name in ('author', 'organization', 'contact', 'address'):
1166 # We attach these to the last author. If any of them precedes
1167 # the first author, put them in a separate "author" group (for
1168 # no better semantics).
1169 if name == 'author' or not self.author_stack:
1170 self.author_stack.append([])
1171 if name == 'address': # newlines are meaningful
1172 self.insert_newline = 1
1173 text = self.encode(node.astext())
1174 self.insert_newline = 0
1175 else:
1176 text = self.attval(node.astext())
1177 self.author_stack[-1].append(text)
1178 raise nodes.SkipNode
1179 elif name == 'date':
1180 self.date = self.attval(node.astext())
1181 raise nodes.SkipNode
1182 self.docinfo.append('\\textbf{%s}: &\n\t' % self.language_label(name))
1183 if name == 'address':
1184 self.insert_newline = 1
1185 self.docinfo.append('{\\raggedright\n')
1186 self.context.append(' } \\\\\n')
1187 else:
1188 self.context.append(' \\\\\n')
1189 self.context.append(self.docinfo)
1190 self.context.append(len(self.body))
1192 def depart_docinfo_item(self, node):
1193 size = self.context.pop()
1194 dest = self.context.pop()
1195 tail = self.context.pop()
1196 tail = self.body[size:] + [tail]
1197 del self.body[size:]
1198 dest.extend(tail)
1199 # for address we did set insert_newline
1200 self.insert_newline = 0
1202 def visit_doctest_block(self, node):
1203 self.body.append( '\\begin{verbatim}' )
1204 self.verbatim = 1
1206 def depart_doctest_block(self, node):
1207 self.body.append( '\\end{verbatim}\n' )
1208 self.verbatim = 0
1210 def visit_document(self, node):
1211 self.body_prefix.append('\\begin{document}\n')
1212 # titled document?
1213 if self.use_latex_docinfo or len(node) and isinstance(node[0], nodes.title):
1214 self.body_prefix.append('\\maketitle\n\n')
1215 # alternative use titlepage environment.
1216 # \begin{titlepage}
1217 self.body.append('\n\\setlength{\\locallinewidth}{\\linewidth}\n')
1219 def depart_document(self, node):
1220 # TODO insertion point of bibliography should none automatic.
1221 if self._use_latex_citations and len(self._bibitems)>0:
1222 widest_label = ""
1223 for bi in self._bibitems:
1224 if len(widest_label)<len(bi[0]):
1225 widest_label = bi[0]
1226 self.body.append('\n\\begin{thebibliography}{%s}\n'%widest_label)
1227 for bi in self._bibitems:
1228 # cite_key: underscores must not be escaped
1229 cite_key = bi[0].replace(r"{\_}","_")
1230 self.body.append('\\bibitem[%s]{%s}{%s}\n' % (bi[0], cite_key, bi[1]))
1231 self.body.append('\\end{thebibliography}\n')
1233 self.body_suffix.append('\\end{document}\n')
1235 def visit_emphasis(self, node):
1236 self.body.append('\\emph{')
1237 self.literal_block_stack.append('\\emph{')
1239 def depart_emphasis(self, node):
1240 self.body.append('}')
1241 self.literal_block_stack.pop()
1243 def visit_entry(self, node):
1244 self.active_table.visit_entry()
1245 # cell separation
1246 if self.active_table.get_entry_number() == 1:
1247 # if the firstrow is a multirow, this actually is the second row.
1248 # this gets hairy if rowspans follow each other.
1249 if self.active_table.get_rowspan(0):
1250 count = 0
1251 while self.active_table.get_rowspan(count):
1252 count += 1
1253 self.body.append(' & ')
1254 self.active_table.visit_entry() # increment cell count
1255 else:
1256 self.body.append(' & ')
1258 # multi{row,column}
1259 # IN WORK BUG TODO HACK continues here
1260 # multirow in LaTeX simply will enlarge the cell over several rows
1261 # (the following n if n is positive, the former if negative).
1262 if node.has_key('morerows') and node.has_key('morecols'):
1263 raise NotImplementedError('Cells that '
1264 'span multiple rows *and* columns are not supported, sorry.')
1265 if node.has_key('morerows'):
1266 count = node['morerows'] + 1
1267 self.active_table.set_rowspan(self.active_table.get_entry_number()-1,count)
1268 self.body.append('\\multirow{%d}{%s}{' % \
1269 (count,self.active_table.get_column_width()))
1270 self.context.append('}')
1271 # BUG following rows must have empty cells.
1272 elif node.has_key('morecols'):
1273 # the vertical bar before column is missing if it is the first column.
1274 # the one after always.
1275 if self.active_table.get_entry_number() == 1:
1276 bar1 = self.active_table.get_vertical_bar()
1277 else:
1278 bar1 = ''
1279 count = node['morecols'] + 1
1280 self.body.append('\\multicolumn{%d}{%sl%s}{' % \
1281 (count, bar1, self.active_table.get_vertical_bar()))
1282 self.context.append('}')
1283 else:
1284 self.context.append('')
1286 # header / not header
1287 if isinstance(node.parent.parent, nodes.thead):
1288 self.body.append('\\textbf{')
1289 self.context.append('}')
1290 else:
1291 self.context.append('')
1293 def depart_entry(self, node):
1294 self.body.append(self.context.pop()) # header / not header
1295 self.body.append(self.context.pop()) # multirow/column
1296 # if following row is spanned from above.
1297 if self.active_table.get_rowspan(self.active_table.get_entry_number()):
1298 self.body.append(' & ')
1299 self.active_table.visit_entry() # increment cell count
1301 def visit_row(self, node):
1302 self.active_table.visit_row()
1304 def depart_row(self, node):
1305 self.body.extend(self.active_table.depart_row())
1307 def visit_enumerated_list(self, node):
1308 # We create our own enumeration list environment.
1309 # This allows to set the style and starting value
1310 # and unlimited nesting.
1311 enum_style = {'arabic':'arabic',
1312 'loweralpha':'alph',
1313 'upperalpha':'Alph',
1314 'lowerroman':'roman',
1315 'upperroman':'Roman' }
1316 enum_suffix = ""
1317 if node.has_key('suffix'):
1318 enum_suffix = node['suffix']
1319 enum_prefix = ""
1320 if node.has_key('prefix'):
1321 enum_prefix = node['prefix']
1322 if self.compound_enumerators:
1323 pref = ""
1324 if self.section_prefix_for_enumerators and self.section_level:
1325 for i in range(self.section_level):
1326 pref += '%d.' % self._section_number[i]
1327 pref = pref[:-1] + self.section_enumerator_separator
1328 enum_prefix += pref
1329 for ctype, cname in self._enumeration_counters:
1330 enum_prefix += '\\%s{%s}.' % (ctype, cname)
1331 enum_type = "arabic"
1332 if node.has_key('enumtype'):
1333 enum_type = node['enumtype']
1334 if enum_style.has_key(enum_type):
1335 enum_type = enum_style[enum_type]
1337 counter_name = "listcnt%d" % len(self._enumeration_counters)
1338 self._enumeration_counters.append((enum_type, counter_name))
1339 # If we haven't used this counter name before, then create a
1340 # new counter; otherwise, reset & reuse the old counter.
1341 if len(self._enumeration_counters) > self._max_enumeration_counters:
1342 self._max_enumeration_counters = len(self._enumeration_counters)
1343 self.body.append('\\newcounter{%s}\n' % counter_name)
1344 else:
1345 self.body.append('\\setcounter{%s}{0}\n' % counter_name)
1347 self.body.append('\\begin{list}{%s\\%s{%s}%s}\n' % \
1348 (enum_prefix,enum_type,counter_name,enum_suffix))
1349 self.body.append('{\n')
1350 self.body.append('\\usecounter{%s}\n' % counter_name)
1351 # set start after usecounter, because it initializes to zero.
1352 if node.has_key('start'):
1353 self.body.append('\\addtocounter{%s}{%d}\n' \
1354 % (counter_name,node['start']-1))
1355 ## set rightmargin equal to leftmargin
1356 self.body.append('\\setlength{\\rightmargin}{\\leftmargin}\n')
1357 self.body.append('}\n')
1359 def depart_enumerated_list(self, node):
1360 self.body.append('\\end{list}\n')
1361 self._enumeration_counters.pop()
1363 def visit_error(self, node):
1364 self.visit_admonition(node, 'error')
1366 def depart_error(self, node):
1367 self.depart_admonition()
1369 def visit_field(self, node):
1370 # real output is done in siblings: _argument, _body, _name
1371 pass
1373 def depart_field(self, node):
1374 self.body.append('\n')
1375 ##self.body.append('%[depart_field]\n')
1377 def visit_field_argument(self, node):
1378 self.body.append('%[visit_field_argument]\n')
1380 def depart_field_argument(self, node):
1381 self.body.append('%[depart_field_argument]\n')
1383 def visit_field_body(self, node):
1384 # BUG by attach as text we loose references.
1385 if self.docinfo:
1386 self.docinfo.append('%s \\\\\n' % self.encode(node.astext()))
1387 raise nodes.SkipNode
1388 # BUG: what happens if not docinfo
1390 def depart_field_body(self, node):
1391 self.body.append( '\n' )
1393 def visit_field_list(self, node):
1394 if not self.docinfo:
1395 self.body.append('\\begin{quote}\n')
1396 self.body.append('\\begin{description}\n')
1398 def depart_field_list(self, node):
1399 if not self.docinfo:
1400 self.body.append('\\end{description}\n')
1401 self.body.append('\\end{quote}\n')
1403 def visit_field_name(self, node):
1404 # BUG this duplicates docinfo_item
1405 if self.docinfo:
1406 self.docinfo.append('\\textbf{%s}: &\n\t' % self.encode(node.astext()))
1407 raise nodes.SkipNode
1408 else:
1409 self.body.append('\\item [')
1411 def depart_field_name(self, node):
1412 if not self.docinfo:
1413 self.body.append(':]')
1415 def visit_figure(self, node):
1416 if (not node.attributes.has_key('align') or
1417 node.attributes['align'] == 'center'):
1418 # centering does not add vertical space like center.
1419 align = '\n\\centering'
1420 align_end = ''
1421 else:
1422 # TODO non vertical space for other alignments.
1423 align = '\\begin{flush%s}' % node.attributes['align']
1424 align_end = '\\end{flush%s}' % node.attributes['align']
1425 self.body.append( '\\begin{figure}[htbp]%s\n' % align )
1426 self.context.append( '%s\\end{figure}\n' % align_end )
1428 def depart_figure(self, node):
1429 self.body.append( self.context.pop() )
1431 def visit_footer(self, node):
1432 self.context.append(len(self.body))
1434 def depart_footer(self, node):
1435 start = self.context.pop()
1436 footer = (['\n\\begin{center}\small\n']
1437 + self.body[start:] + ['\n\\end{center}\n'])
1438 self.body_suffix[:0] = footer
1439 del self.body[start:]
1441 def visit_footnote(self, node):
1442 if self.use_latex_footnotes:
1443 num,text = node.astext().split(None,1)
1444 num = self.encode(num.strip())
1445 self.body.append('\\footnotetext['+num+']')
1446 self.body.append('{')
1447 else:
1448 self.body.append('\\begin{figure}[b]')
1449 for id in node['ids']:
1450 self.body.append('\\hypertarget{%s}' % id)
1452 def depart_footnote(self, node):
1453 if self.use_latex_footnotes:
1454 self.body.append('}\n')
1455 else:
1456 self.body.append('\\end{figure}\n')
1458 def visit_footnote_reference(self, node):
1459 if self.use_latex_footnotes:
1460 self.body.append("\\footnotemark["+self.encode(node.astext())+"]")
1461 raise nodes.SkipNode
1462 href = ''
1463 if node.has_key('refid'):
1464 href = node['refid']
1465 elif node.has_key('refname'):
1466 href = self.document.nameids[node['refname']]
1467 format = self.settings.footnote_references
1468 if format == 'brackets':
1469 suffix = '['
1470 self.context.append(']')
1471 elif format == 'superscript':
1472 suffix = '\\raisebox{.5em}[0em]{\\scriptsize'
1473 self.context.append('}')
1474 else: # shouldn't happen
1475 raise AssertionError('Illegal footnote reference format.')
1476 self.body.append('%s\\hyperlink{%s}{' % (suffix,href))
1478 def depart_footnote_reference(self, node):
1479 if self.use_latex_footnotes:
1480 return
1481 self.body.append('}%s' % self.context.pop())
1483 # footnote/citation label
1484 def label_delim(self, node, bracket, superscript):
1485 if isinstance(node.parent, nodes.footnote):
1486 if self.use_latex_footnotes:
1487 raise nodes.SkipNode
1488 if self.settings.footnote_references == 'brackets':
1489 self.body.append(bracket)
1490 else:
1491 self.body.append(superscript)
1492 else:
1493 assert isinstance(node.parent, nodes.citation)
1494 if not self._use_latex_citations:
1495 self.body.append(bracket)
1497 def visit_label(self, node):
1498 self.label_delim(node, '[', '$^{')
1500 def depart_label(self, node):
1501 self.label_delim(node, ']', '}$')
1503 # elements generated by the framework e.g. section numbers.
1504 def visit_generated(self, node):
1505 pass
1507 def depart_generated(self, node):
1508 pass
1510 def visit_header(self, node):
1511 self.context.append(len(self.body))
1513 def depart_header(self, node):
1514 start = self.context.pop()
1515 self.body_prefix.append('\n\\verb|begin_header|\n')
1516 self.body_prefix.extend(self.body[start:])
1517 self.body_prefix.append('\n\\verb|end_header|\n')
1518 del self.body[start:]
1520 def visit_hint(self, node):
1521 self.visit_admonition(node, 'hint')
1523 def depart_hint(self, node):
1524 self.depart_admonition()
1526 def latex_image_length(self, width_str):
1527 match = re.match('(\d*\.?\d*)\s*(\S*)', width_str)
1528 if not match:
1529 # fallback
1530 return width_str
1531 res = width_str
1532 amount, unit = match.groups()[:2]
1533 if unit == "px":
1534 # LaTeX does not know pixels but points
1535 res = "%spt" % amount
1536 elif unit == "%":
1537 res = "%.3f\\linewidth" % (float(amount)/100.0)
1538 return res
1540 def visit_image(self, node):
1541 attrs = node.attributes
1542 # Add image URI to dependency list, assuming that it's
1543 # referring to a local file.
1544 self.settings.record_dependencies.add(attrs['uri'])
1545 pre = [] # in reverse order
1546 post = []
1547 include_graphics_options = []
1548 inline = isinstance(node.parent, nodes.TextElement)
1549 if attrs.has_key('scale'):
1550 # Could also be done with ``scale`` option to
1551 # ``\includegraphics``; doing it this way for consistency.
1552 pre.append('\\scalebox{%f}{' % (attrs['scale'] / 100.0,))
1553 post.append('}')
1554 if attrs.has_key('width'):
1555 include_graphics_options.append('width=%s' % (
1556 self.latex_image_length(attrs['width']), ))
1557 if attrs.has_key('height'):
1558 include_graphics_options.append('height=%s' % (
1559 self.latex_image_length(attrs['height']), ))
1560 if attrs.has_key('align'):
1561 align_prepost = {
1562 # By default latex aligns the top of an image.
1563 (1, 'top'): ('', ''),
1564 (1, 'middle'): ('\\raisebox{-0.5\\height}{', '}'),
1565 (1, 'bottom'): ('\\raisebox{-\\height}{', '}'),
1566 (0, 'center'): ('{\\hfill', '\\hfill}'),
1567 # These 2 don't exactly do the right thing. The image should
1568 # be floated alongside the paragraph. See
1569 # http://www.w3.org/TR/html4/struct/objects.html#adef-align-IMG
1570 (0, 'left'): ('{', '\\hfill}'),
1571 (0, 'right'): ('{\\hfill', '}'),}
1572 try:
1573 pre.append(align_prepost[inline, attrs['align']][0])
1574 post.append(align_prepost[inline, attrs['align']][1])
1575 except KeyError:
1576 pass # XXX complain here?
1577 if not inline:
1578 pre.append('\n')
1579 post.append('\n')
1580 pre.reverse()
1581 self.body.extend( pre )
1582 options = ''
1583 if len(include_graphics_options)>0:
1584 options = '[%s]' % (','.join(include_graphics_options))
1585 self.body.append( '\\includegraphics%s{%s}' % (
1586 options, attrs['uri'] ) )
1587 self.body.extend( post )
1589 def depart_image(self, node):
1590 pass
1592 def visit_important(self, node):
1593 self.visit_admonition(node, 'important')
1595 def depart_important(self, node):
1596 self.depart_admonition()
1598 def visit_interpreted(self, node):
1599 # @@@ Incomplete, pending a proper implementation on the
1600 # Parser/Reader end.
1601 self.visit_literal(node)
1603 def depart_interpreted(self, node):
1604 self.depart_literal(node)
1606 def visit_legend(self, node):
1607 self.body.append('{\\small ')
1609 def depart_legend(self, node):
1610 self.body.append('}')
1612 def visit_line(self, node):
1613 self.body.append('\item[] ')
1615 def depart_line(self, node):
1616 self.body.append('\n')
1618 def visit_line_block(self, node):
1619 if isinstance(node.parent, nodes.line_block):
1620 self.body.append('\\item[] \n'
1621 '\\begin{lineblock}{\\lineblockindentation}\n')
1622 else:
1623 self.body.append('\n\\begin{lineblock}{0em}\n')
1625 def depart_line_block(self, node):
1626 self.body.append('\\end{lineblock}\n')
1628 def visit_list_item(self, node):
1629 # Append "{}" in case the next character is "[", which would break
1630 # LaTeX's list environment (no numbering and the "[" is not printed).
1631 self.body.append('\\item {} ')
1633 def depart_list_item(self, node):
1634 self.body.append('\n')
1636 def visit_literal(self, node):
1637 self.literal = 1
1638 self.body.append('\\texttt{')
1640 def depart_literal(self, node):
1641 self.body.append('}')
1642 self.literal = 0
1644 def visit_literal_block(self, node):
1646 Render a literal-block.
1648 Literal blocks are used for "::"-prefixed literal-indented
1649 blocks of text, where the inline markup is not recognized,
1650 but are also the product of the parsed-literal directive,
1651 where the markup is respected.
1653 # In both cases, we want to use a typewriter/monospaced typeface.
1654 # For "real" literal-blocks, we can use \verbatim, while for all
1655 # the others we must use \mbox.
1657 # We can distinguish between the two kinds by the number of
1658 # siblings that compose this node: if it is composed by a
1659 # single element, it's surely either a real one or a
1660 # parsed-literal that does not contain any markup.
1662 if not self.active_table.is_open():
1663 # no quote inside tables, to avoid vertical space between
1664 # table border and literal block.
1665 # BUG: fails if normal text preceeds the literal block.
1666 self.body.append('\\begin{quote}')
1667 else:
1668 self.body.append('\n')
1669 if (self.settings.use_verbatim_when_possible and (len(node) == 1)
1670 # in case of a parsed-literal containing just a "**bold**" word:
1671 and isinstance(node[0], nodes.Text)):
1672 self.verbatim = 1
1673 self.body.append('\\begin{verbatim}\n')
1674 else:
1675 self.literal_block = 1
1676 self.insert_none_breaking_blanks = 1
1677 self.body.append('{\\ttfamily \\raggedright \\noindent\n')
1678 # * obey..: is from julien and never worked for me (grubert).
1679 # self.body.append('{\\obeylines\\obeyspaces\\ttfamily\n')
1681 def depart_literal_block(self, node):
1682 if self.verbatim:
1683 self.body.append('\n\\end{verbatim}\\end{quote}\n')
1684 self.verbatim = 0
1685 else:
1686 if self.active_table.is_open():
1687 self.body.append('\n}\n')
1688 else:
1689 self.body.append('\n')
1690 self.body.append('}\\end{quote}\n')
1691 self.insert_none_breaking_blanks = 0
1692 self.literal_block = 0
1693 # obey end: self.body.append('}\n')
1695 def visit_meta(self, node):
1696 self.body.append('[visit_meta]\n')
1697 # BUG maybe set keywords for pdf
1698 ##self.head.append(self.starttag(node, 'meta', **node.attributes))
1700 def depart_meta(self, node):
1701 self.body.append('[depart_meta]\n')
1703 def visit_note(self, node):
1704 self.visit_admonition(node, 'note')
1706 def depart_note(self, node):
1707 self.depart_admonition()
1709 def visit_option(self, node):
1710 if self.context[-1]:
1711 # this is not the first option
1712 self.body.append(', ')
1714 def depart_option(self, node):
1715 # flag tha the first option is done.
1716 self.context[-1] += 1
1718 def visit_option_argument(self, node):
1719 """The delimiter betweeen an option and its argument."""
1720 self.body.append(node.get('delimiter', ' '))
1722 def depart_option_argument(self, node):
1723 pass
1725 def visit_option_group(self, node):
1726 if self.use_optionlist_for_option_list:
1727 self.body.append('\\item [')
1728 else:
1729 if len(node.astext()) > 14:
1730 self.body.append('\\multicolumn{2}{l}{')
1731 self.context.append('} \\\\\n ')
1732 else:
1733 self.context.append('')
1734 self.body.append('\\texttt{')
1735 # flag for first option
1736 self.context.append(0)
1738 def depart_option_group(self, node):
1739 self.context.pop() # the flag
1740 if self.use_optionlist_for_option_list:
1741 self.body.append('] ')
1742 else:
1743 self.body.append('}')
1744 self.body.append(self.context.pop())
1746 def visit_option_list(self, node):
1747 self.body.append('% [option list]\n')
1748 if self.use_optionlist_for_option_list:
1749 self.body.append('\\begin{optionlist}{3cm}\n')
1750 else:
1751 self.body.append('\\begin{center}\n')
1752 # BUG: use admwidth or make it relative to textwidth ?
1753 self.body.append('\\begin{tabularx}{.9\\linewidth}{lX}\n')
1755 def depart_option_list(self, node):
1756 if self.use_optionlist_for_option_list:
1757 self.body.append('\\end{optionlist}\n')
1758 else:
1759 self.body.append('\\end{tabularx}\n')
1760 self.body.append('\\end{center}\n')
1762 def visit_option_list_item(self, node):
1763 pass
1765 def depart_option_list_item(self, node):
1766 if not self.use_optionlist_for_option_list:
1767 self.body.append('\\\\\n')
1769 def visit_option_string(self, node):
1770 ##self.body.append(self.starttag(node, 'span', '', CLASS='option'))
1771 pass
1773 def depart_option_string(self, node):
1774 ##self.body.append('</span>')
1775 pass
1777 def visit_organization(self, node):
1778 self.visit_docinfo_item(node, 'organization')
1780 def depart_organization(self, node):
1781 self.depart_docinfo_item(node)
1783 def visit_paragraph(self, node):
1784 index = node.parent.index(node)
1785 if not ('contents' in self.topic_classes or
1786 (isinstance(node.parent, nodes.compound) and
1787 index > 0 and
1788 not isinstance(node.parent[index - 1], nodes.paragraph) and
1789 not isinstance(node.parent[index - 1], nodes.compound))):
1790 self.body.append('\n')
1792 def depart_paragraph(self, node):
1793 self.body.append('\n')
1795 def visit_problematic(self, node):
1796 self.body.append('{\\color{red}\\bfseries{}')
1798 def depart_problematic(self, node):
1799 self.body.append('}')
1801 def visit_raw(self, node):
1802 if 'latex' in node.get('format', '').split():
1803 self.body.append(node.astext())
1804 raise nodes.SkipNode
1806 def visit_reference(self, node):
1807 # BUG: hash_char "#" is trouble some in LaTeX.
1808 # mbox and other environment do not like the '#'.
1809 hash_char = '\\#'
1810 if node.has_key('refuri'):
1811 href = node['refuri'].replace('#',hash_char)
1812 elif node.has_key('refid'):
1813 href = hash_char + node['refid']
1814 elif node.has_key('refname'):
1815 href = hash_char + self.document.nameids[node['refname']]
1816 else:
1817 raise AssertionError('Unknown reference.')
1818 self.body.append('\\href{%s}{' % href)
1820 def depart_reference(self, node):
1821 self.body.append('}')
1823 def visit_revision(self, node):
1824 self.visit_docinfo_item(node, 'revision')
1826 def depart_revision(self, node):
1827 self.depart_docinfo_item(node)
1829 def visit_section(self, node):
1830 self.section_level += 1
1831 # Initialize counter for potential subsections:
1832 self._section_number.append(0)
1833 # Counter for this section's level (initialized by parent section):
1834 self._section_number[self.section_level - 1] += 1
1836 def depart_section(self, node):
1837 # Remove counter for potential subsections:
1838 self._section_number.pop()
1839 self.section_level -= 1
1841 def visit_sidebar(self, node):
1842 # BUG: this is just a hack to make sidebars render something
1843 self.body.append('\n\\setlength{\\locallinewidth}{0.9\\admonitionwidth}\n')
1844 self.body.append('\\begin{center}\\begin{sffamily}\n')
1845 self.body.append('\\fbox{\\colorbox[gray]{0.80}{\\parbox{\\admonitionwidth}{\n')
1847 def depart_sidebar(self, node):
1848 self.body.append('}}}\n') # end parbox colorbox fbox
1849 self.body.append('\\end{sffamily}\n\\end{center}\n');
1850 self.body.append('\n\\setlength{\\locallinewidth}{\\linewidth}\n')
1853 attribution_formats = {'dash': ('---', ''),
1854 'parentheses': ('(', ')'),
1855 'parens': ('(', ')'),
1856 'none': ('', '')}
1858 def visit_attribution(self, node):
1859 prefix, suffix = self.attribution_formats[self.settings.attribution]
1860 self.body.append('\n\\begin{flushright}\n')
1861 self.body.append(prefix)
1862 self.context.append(suffix)
1864 def depart_attribution(self, node):
1865 self.body.append(self.context.pop() + '\n')
1866 self.body.append('\\end{flushright}\n')
1868 def visit_status(self, node):
1869 self.visit_docinfo_item(node, 'status')
1871 def depart_status(self, node):
1872 self.depart_docinfo_item(node)
1874 def visit_strong(self, node):
1875 self.body.append('\\textbf{')
1876 self.literal_block_stack.append('\\textbf{')
1878 def depart_strong(self, node):
1879 self.body.append('}')
1880 self.literal_block_stack.pop()
1882 def visit_substitution_definition(self, node):
1883 raise nodes.SkipNode
1885 def visit_substitution_reference(self, node):
1886 self.unimplemented_visit(node)
1888 def visit_subtitle(self, node):
1889 if isinstance(node.parent, nodes.sidebar):
1890 self.body.append('~\\\\\n\\textbf{')
1891 self.context.append('}\n\\smallskip\n')
1892 elif isinstance(node.parent, nodes.document):
1893 self.title = self.title + \
1894 '\\\\\n\\large{%s}\n' % self.encode(node.astext())
1895 raise nodes.SkipNode
1896 elif isinstance(node.parent, nodes.section):
1897 self.body.append('\\textbf{')
1898 self.context.append('}\\vspace{0.2cm}\n\n\\noindent ')
1900 def depart_subtitle(self, node):
1901 self.body.append(self.context.pop())
1903 def visit_system_message(self, node):
1904 pass
1906 def depart_system_message(self, node):
1907 self.body.append('\n')
1909 def visit_table(self, node):
1910 if self.active_table.is_open():
1911 print 'nested tables are not supported'
1912 raise AssertionError
1913 self.active_table.open()
1914 for cl in node['classes']:
1915 self.active_table.set_table_style(cl)
1916 self.body.append('\n' + self.active_table.get_opening())
1918 def depart_table(self, node):
1919 self.body.append(self.active_table.get_closing() + '\n')
1920 self.active_table.close()
1921 self.active_table.set_table_style(self.settings.table_style)
1923 def visit_target(self, node):
1924 # BUG: why not (refuri or refid or refname) means not footnote ?
1925 if not (node.has_key('refuri') or node.has_key('refid')
1926 or node.has_key('refname')):
1927 for id in node['ids']:
1928 self.body.append('\\hypertarget{%s}{' % id)
1929 self.context.append('}' * len(node['ids']))
1930 elif node.get("refid"):
1931 self.body.append('\\hypertarget{%s}{' % node.get("refid"))
1932 self.context.append('}')
1933 else:
1934 self.context.append('')
1936 def depart_target(self, node):
1937 self.body.append(self.context.pop())
1939 def visit_tbody(self, node):
1940 # BUG write preamble if not yet done (colspecs not [])
1941 # for tables without heads.
1942 if not self.active_table.get('preamble written'):
1943 self.visit_thead(None)
1944 # self.depart_thead(None)
1946 def depart_tbody(self, node):
1947 pass
1949 def visit_term(self, node):
1950 self.body.append('\\item[{')
1952 def depart_term(self, node):
1953 # definition list term.
1954 self.body.append('}] ')
1956 def visit_tgroup(self, node):
1957 #self.body.append(self.starttag(node, 'colgroup'))
1958 #self.context.append('</colgroup>\n')
1959 pass
1961 def depart_tgroup(self, node):
1962 pass
1964 def visit_thead(self, node):
1965 self.body.append('{%s}\n' % self.active_table.get_colspecs())
1966 if self.active_table.caption:
1967 self.body.append('\\caption{%s}\\\\\n' % self.active_table.caption)
1968 self.active_table.set('preamble written',1)
1969 # TODO longtable supports firsthead and lastfoot too.
1970 self.body.extend(self.active_table.visit_thead())
1972 def depart_thead(self, node):
1973 # the table header written should be on every page
1974 # => \endhead
1975 self.body.extend(self.active_table.depart_thead())
1976 # and the firsthead => \endfirsthead
1977 # BUG i want a "continued from previous page" on every not
1978 # firsthead, but then we need the header twice.
1980 # there is a \endfoot and \endlastfoot too.
1981 # but we need the number of columns to
1982 # self.body.append('\\multicolumn{%d}{c}{"..."}\n' % number_of_columns)
1983 # self.body.append('\\hline\n\\endfoot\n')
1984 # self.body.append('\\hline\n')
1985 # self.body.append('\\endlastfoot\n')
1987 def visit_tip(self, node):
1988 self.visit_admonition(node, 'tip')
1990 def depart_tip(self, node):
1991 self.depart_admonition()
1993 def bookmark(self, node):
1994 """Append latex href and pdfbookmarks for titles.
1996 if node.parent['ids']:
1997 for id in node.parent['ids']:
1998 self.body.append('\\hypertarget{%s}{}\n' % id)
1999 if not self.use_latex_toc:
2000 # BUG level depends on style. pdflatex allows level 0 to 3
2001 # ToC would be the only on level 0 so i choose to decrement the rest.
2002 # "Table of contents" bookmark to see the ToC. To avoid this
2003 # we set all zeroes to one.
2004 l = self.section_level
2005 if l>0:
2006 l = l-1
2007 # pdftex does not like "_" subscripts in titles
2008 text = self.encode(node.astext())
2009 for id in node.parent['ids']:
2010 self.body.append('\\pdfbookmark[%d]{%s}{%s}\n' % \
2011 (l, text, id))
2013 def visit_title(self, node):
2014 """Only 3 section levels are supported by LaTeX article (AFAIR)."""
2016 if isinstance(node.parent, nodes.topic):
2017 # the table of contents.
2018 self.bookmark(node)
2019 if ('contents' in self.topic_classes
2020 and self.use_latex_toc):
2021 self.body.append('\\renewcommand{\\contentsname}{')
2022 self.context.append('}\n\\tableofcontents\n\n\\bigskip\n')
2023 elif ('abstract' in self.topic_classes
2024 and self.settings.use_latex_abstract):
2025 raise nodes.SkipNode
2026 else: # or section titles before the table of contents.
2027 # BUG: latex chokes on center environment with
2028 # "perhaps a missing item", therefore we use hfill.
2029 self.body.append('\\subsubsection*{~\\hfill ')
2030 # the closing brace for subsection.
2031 self.context.append('\\hfill ~}\n')
2032 # TODO: for admonition titles before the first section
2033 # either specify every possible node or ... ?
2034 elif isinstance(node.parent, nodes.sidebar) \
2035 or isinstance(node.parent, nodes.admonition):
2036 self.body.append('\\textbf{\\large ')
2037 self.context.append('}\n\\smallskip\n')
2038 elif isinstance(node.parent, nodes.table):
2039 # caption must be written after column spec
2040 self.active_table.caption = self.encode(node.astext())
2041 raise nodes.SkipNode
2042 elif self.section_level == 0:
2043 # document title
2044 self.title = self.encode(node.astext())
2045 if not self.pdfinfo == None:
2046 self.pdfinfo.append( 'pdftitle={%s}' % self.encode(node.astext()) )
2047 raise nodes.SkipNode
2048 else:
2049 self.body.append('\n\n')
2050 self.body.append('%' + '_' * 75)
2051 self.body.append('\n\n')
2052 self.bookmark(node)
2054 if self.use_latex_toc:
2055 section_star = ""
2056 else:
2057 section_star = "*"
2059 section_name = self.d_class.section(self.section_level)
2060 self.body.append('\\%s%s{' % (section_name, section_star))
2062 self.context.append('}\n')
2064 def depart_title(self, node):
2065 self.body.append(self.context.pop())
2067 def visit_topic(self, node):
2068 self.topic_classes = node['classes']
2069 if ('abstract' in self.topic_classes
2070 and self.settings.use_latex_abstract):
2071 self.body.append('\\begin{abstract}\n')
2073 def depart_topic(self, node):
2074 if ('abstract' in self.topic_classes
2075 and self.settings.use_latex_abstract):
2076 self.body.append('\\end{abstract}\n')
2077 self.topic_classes = []
2078 if 'contents' in node['classes'] and self.use_latex_toc:
2079 pass
2080 else:
2081 self.body.append('\n')
2083 def visit_inline(self, node): # titlereference
2084 classes = node.get('classes', ['Unknown', ])
2085 for cls in classes:
2086 self.body.append( '\\docutilsrole%s{' % cls)
2087 self.context.append('}'*len(classes))
2089 def depart_inline(self, node):
2090 self.body.append(self.context.pop())
2092 def visit_rubric(self, node):
2093 self.body.append('\\rubric{')
2094 self.context.append('}\n')
2096 def depart_rubric(self, node):
2097 self.body.append(self.context.pop())
2099 def visit_transition(self, node):
2100 self.body.append('\n\n')
2101 self.body.append('%' + '_' * 75)
2102 self.body.append('\n\\hspace*{\\fill}\\hrulefill\\hspace*{\\fill}')
2103 self.body.append('\n\n')
2105 def depart_transition(self, node):
2106 pass
2108 def visit_version(self, node):
2109 self.visit_docinfo_item(node, 'version')
2111 def depart_version(self, node):
2112 self.depart_docinfo_item(node)
2114 def visit_warning(self, node):
2115 self.visit_admonition(node, 'warning')
2117 def depart_warning(self, node):
2118 self.depart_admonition()
2120 def unimplemented_visit(self, node):
2121 raise NotImplementedError('visiting unimplemented node type: %s'
2122 % node.__class__.__name__)
2124 # def unknown_visit(self, node):
2125 # def default_visit(self, node):
2127 # vim: set ts=4 et ai :