2 # Author: Engelbert Gruber <grubert@users.sourceforge.net>
3 # Copyright: This module has been placed in the public domain.
6 LaTeX2e document tree Writer.
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. ##.
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."""
30 'LaTeX-Specific Options',
31 'The LaTeX "--output-encoding" default is "latin-1:strict".',
32 (('Specify documentclass. Default is "article".',
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".',
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.',
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 '
75 {'default': 0, 'action': 'store_true',
76 'validator': frontend
.validate_boolean
}),
77 ('Let LaTeX print author and date, do not show it in docutils '
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 '
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"',
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'],
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).',
140 settings_defaults
= {'output_encoding': 'latin-1'}
142 relative_path_settings
= ('stylesheet_path',)
144 config_section
= 'latex2e writer'
145 config_section_dependencies
= ('writers',)
148 """Final translated form of `document`."""
151 writers
.Writer
.__init
__(self
)
152 self
.translator_class
= LaTeXTranslator
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
168 * LaTeX does not support multiple tocs in one document.
169 (might be no limitation except for docutils documentation)
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
182 So we add locallinewidth set it initially and
183 on entering sidebar and reset on exit.
187 """Language specifics for LaTeX."""
188 # country code by a.schlock.
189 # partly manually converted from iso and babel stuff, dialects and some
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
204 # french, francais, canadien, acadian
205 'de': 'ngerman', #XXX rather than german
206 # ngerman, naustrian, german, germanb, austrian
209 # english, USenglish, american, UKenglish, british, canadian
235 def __init__(self
,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`\"}'
247 self
.quotes
= ("``", "''")
250 def next_quote(self
):
251 q
= self
.quotes
[self
.quote_index
]
252 self
.quote_index
= (self
.quote_index
+1)%2
255 def quote_quotes(self
,text
):
257 for part
in text
.split('"'):
261 t
+= self
.next_quote() + part
264 def double_quotes_in_tt (self
,text
):
265 if not self
.double_quote_replacment
:
267 return text
.replace('"', self
.double_quote_replacment
)
269 def get_language(self
):
270 if self
._ISO
639_TO
_BABEL
.has_key(self
.language
):
271 return self
._ISO
639_TO
_BABEL
[self
.language
]
274 l
= self
.language
.split("_")[0]
275 if self
._ISO
639_TO
_BABEL
.has_key(l
):
276 return self
._ISO
639_TO
_BABEL
[l
]
281 'optionlist_environment' : [
282 '\\newcommand{\\optionlistlabel}[1]{\\bf #1 \\hfill}\n'
283 '\\newenvironment{optionlist}[1]\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'
293 'lineblock_environment' : [
294 '\\newlength{\\lineblockindentation}\n'
295 '\\setlength{\\lineblockindentation}{2.5em}\n'
296 '\\newenvironment{lineblock}[1]\n'
298 ' {\\setlength{\\partopsep}{\\parskip}\n'
299 ' \\addtolength{\\partopsep}{\\baselineskip}\n'
300 ' \\topsep0pt\\itemsep0.15\\baselineskip\\parsep0pt\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',
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',
328 """Details of a LaTeX document class."""
330 # BUG: LaTeX has no deeper sections (actually paragrah is no
332 # BUG: No support for unknown document classes. Make 'article'
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
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]
357 return self
._deepest
_section
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.
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
374 # miscellaneous attributes
384 self
._in
_head
= 0 # maybe context with search
387 self
._col
_specs
= None
392 def set_table_style(self
, table_style
):
393 if not table_style
in ('standard','booktabs','borderless','nolines'):
395 self
._table
_style
= table_style
397 def used_packages(self
):
398 if self
._table
_style
== 'booktabs':
399 return '\\usepackage{booktabs}\n'
401 def get_latex_type(self
):
402 return self
._latex
_type
404 def set(self
,attr
,value
):
405 self
._attrs
[attr
] = value
407 if self
._attrs
.has_key(attr
):
408 return self
._attrs
[attr
]
410 def get_vertical_bar(self
):
411 if self
._table
_style
== 'standard':
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
):
419 if self
._table
_style
== 'booktabs':
420 line
= '\\bottomrule\n'
421 elif self
._table
_style
== 'standard':
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.
439 usually gets to narrow, therefore we add 1 (fiddlefactor).
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
450 # donot make it full linewidth
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
):
470 if self
._table
_style
== 'standard':
472 elif self
._table
_style
== 'booktabs':
473 return ['\\toprule\n']
475 def depart_thead(self
):
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
486 self
._cell
_in
_row
= 0
487 def depart_row(self
):
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':
496 for i
in range(len(self
._rowspan
)):
497 if (self
._rowspan
[i
]<=0):
499 if len(rowspans
)==len(self
._rowspan
):
500 res
.append('\\hline\n')
507 c_start
= rowspans
.pop()
510 cline
+= '\\cline{%d-%d}\n' % (c_start
,c_start
)
514 def set_rowspan(self
,cell
,value
):
516 self
._rowspan
[cell
] = value
519 def get_rowspan(self
,cell
):
521 return self
._rowspan
[cell
]
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.
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
= '-'
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'
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'
604 if self
.d_options
.find('DIV') == -1 and self
.d_options
.find('BCOR') == -1:
605 self
.typearea
= '\\typearea{12}\n'
609 if self
.font_encoding
== 'OT1':
611 elif self
.font_encoding
== '':
612 fontenc_header
= '\\usepackage{ae}\n\\usepackage{aeguill}\n'
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}',
624 ' \\usepackage[pdftex]{graphicx}',
627 self
.graphicx_package
= (
628 '\\usepackage[%s]{graphicx}\n' % self
.settings
.graphicx_option
)
631 self
.latex_head
% (self
.d_options
,self
.settings
.documentclass
),
632 '\\usepackage{babel}\n', # language is in documents settings.
634 '\\usepackage{shortvrb}\n', # allows verb in footnotes.
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.
642 # * ltxtable is a combination of tabularx and longtable (pagebreaks).
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
),
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',
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
)
674 settings
.record_dependencies
.add(stylesheet
)
675 self
.head_prefix
.append(self
.stylesheet
% (stylesheet
))
677 if self
.linking
: # and maybe check for pdf
679 self
.pdfauthor
= None
680 # pdftitle, pdfsubject, pdfauthor, pdfkeywords, pdfcreator, pdfproducer
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.
689 # separate title, so we can appen subtitle.
691 # if use_latex_docinfo: collects lists of author/organization/contact/address lines
692 self
.author_stack
= []
695 self
.body_prefix
= ['\\raggedbottom\n']
697 self
.body_suffix
= ['\n']
698 self
.section_level
= 0
700 self
.topic_classes
= []
701 # column specification for tables
702 self
.table_caption
= None
706 # verbatim: to tell encode not to encode.
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
735 # inside literal block: no quote mangling.
736 self
.literal_block
= 0
737 self
.literal_block_stack
= []
739 # true when encoding in math mode
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
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
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
= {
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
):
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
])
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.
821 # compile the regexps once. do it here so one can see them.
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
)
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}')
843 text
= text
.replace("&", '{\\&}')
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.)
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
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
866 text
= text
.replace('_', '{\\underline{ }}')
867 # And the tt-backslash doesn't work in OT1, so we use
869 text
= text
.replace('\\textbackslash', '\\reflectbox{/}')
871 text
= text
.replace('_', '{\\_}')
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
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
)
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
)
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
))
905 if self
.pdfinfo
is not None:
907 self
.pdfinfo
.append('pdfauthor={%s}' % self
.pdfauthor
)
909 pdfinfo
= '\\hypersetup{\n' + ',\n'.join(self
.pdfinfo
) + '\n}\n'
912 head
= '\\title{%s}\n\\author{%s}\n\\date{%s}\n' % \
914 ' \\and\n'.join(['~\\\\\n'.join(author_lines
)
915 for author_lines
in self
.author_stack
]),
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
):
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')
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.
960 def depart_authors(self
, node
):
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
:
973 self
.body
.append( '\\begin{list}{}{}\n' )
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' )
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('$^{')
989 def depart_superscript(self
, node
):
990 self
.body
.append('}$')
993 def visit_subscript(self
, node
):
994 self
.body
.append('$_{')
997 def depart_subscript(self
, node
):
998 self
.body
.append('}$')
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
))
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
])
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
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
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
):
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
):
1077 def depart_compound(self
, node
):
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
):
1089 def depart_container(self
, node
):
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
):
1113 def depart_decoration(self
, node
):
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( ' ' )
1139 self
.body
.append( ' & ' )
1141 def depart_description(self
, node
):
1144 def visit_docinfo(self
, node
):
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.
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())
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
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')
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
:]
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}' )
1206 def depart_doctest_block(self
, node
):
1207 self
.body
.append( '\\end{verbatim}\n' )
1210 def visit_document(self
, node
):
1211 self
.body_prefix
.append('\\begin{document}\n')
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.
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:
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()
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):
1251 while self
.active_table
.get_rowspan(count
):
1253 self
.body
.append(' & ')
1254 self
.active_table
.visit_entry() # increment cell count
1256 self
.body
.append(' & ')
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()
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('}')
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('}')
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' }
1317 if node
.has_key('suffix'):
1318 enum_suffix
= node
['suffix']
1320 if node
.has_key('prefix'):
1321 enum_prefix
= node
['prefix']
1322 if self
.compound_enumerators
:
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
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
)
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
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.
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
1406 self
.docinfo
.append('\\textbf{%s}: &\n\t' % self
.encode(node
.astext()))
1407 raise nodes
.SkipNode
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'
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('{')
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')
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
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':
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
:
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
)
1491 self
.body
.append(superscript
)
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
):
1507 def depart_generated(self
, node
):
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
)
1532 amount
, unit
= match
.groups()[:2]
1534 # LaTeX does not know pixels but points
1535 res
= "%spt" % amount
1537 res
= "%.3f\\linewidth" % (float(amount
)/100.0)
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
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,))
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'):
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', '}'),}
1573 pre
.append(align_prepost
[inline
, attrs
['align']][0])
1574 post
.append(align_prepost
[inline
, attrs
['align']][1])
1576 pass # XXX complain here?
1581 self
.body
.extend( pre
)
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
):
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')
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
):
1638 self
.body
.append('\\texttt{')
1640 def depart_literal(self
, node
):
1641 self
.body
.append('}')
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}')
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
)):
1673 self
.body
.append('\\begin{verbatim}\n')
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
):
1683 self
.body
.append('\n\\end{verbatim}\\end{quote}\n')
1686 if self
.active_table
.is_open():
1687 self
.body
.append('\n}\n')
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
):
1725 def visit_option_group(self
, node
):
1726 if self
.use_optionlist_for_option_list
:
1727 self
.body
.append('\\item [')
1729 if len(node
.astext()) > 14:
1730 self
.body
.append('\\multicolumn{2}{l}{')
1731 self
.context
.append('} \\\\\n ')
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('] ')
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')
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')
1759 self
.body
.append('\\end{tabularx}\n')
1760 self
.body
.append('\\end{center}\n')
1762 def visit_option_list_item(self
, node
):
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'))
1773 def depart_option_string(self
, node
):
1774 ##self.body.append('</span>')
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
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 '#'.
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']]
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': ('(', ')'),
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
):
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('}')
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
):
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')
1961 def depart_tgroup(self
, node
):
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
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
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' % \
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.
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:
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
2049 self
.body
.append('\n\n')
2050 self
.body
.append('%' + '_' * 75)
2051 self
.body
.append('\n\n')
2054 if self
.use_latex_toc
:
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
:
2081 self
.body
.append('\n')
2083 def visit_inline(self
, node
): # titlereference
2084 classes
= node
.get('classes', ['Unknown', ])
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
):
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 :