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