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
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."""
32 'LaTeX-Specific Options',
33 'The LaTeX "--output-encoding" default is "latin-1:strict".',
34 (('Specify documentclass. Default is "article".',
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".',
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.',
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 '
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 '
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 '
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"',
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'],
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).',
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".',
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")
166 """Final translated form of `document`."""
169 writers
.Writer
.__init
__(self
)
170 self
.translator_class
= LaTeXTranslator
173 visitor
= self
.translator_class(self
.document
)
174 self
.document
.walkabout(visitor
)
175 self
.output
= visitor
.astext()
176 self
.head_prefix
= visitor
.head_prefix
177 self
.head
= visitor
.head
178 self
.body_prefix
= visitor
.body_prefix
179 self
.body
= visitor
.body
180 self
.body_suffix
= visitor
.body_suffix
182 def assemble_parts(self
):
183 writers
.Writer
.assemble_parts(self
)
184 for part
in self
.visitor_attributes
:
185 self
.parts
[part
] = ''.join(getattr(self
, part
))
192 * LaTeX does not support multiple tocs in one document.
193 (might be no limitation except for docutils documentation)
197 * linewidth - width of a line in the local environment
198 * textwidth - the width of text on the page
200 Maybe always use linewidth ?
202 *Bug* inside a minipage a (e.g. Sidebar) the linewidth is
203 not changed, needs fix in docutils so that tables
206 So we add locallinewidth set it initially and
207 on entering sidebar and reset on exit.
211 """Language specifics for LaTeX."""
212 # country code by a.schlock.
213 # partly manually converted from iso and babel stuff, dialects and some
215 'no': 'norsk', #XXX added by hand ( forget about nynorsk?)
216 'gd': 'scottish', #XXX added by hand
217 'hu': 'magyar', #XXX added by hand
218 'pt': 'portuguese',#XXX added by hand
228 # french, francais, canadien, acadian
229 'de': 'ngerman', #XXX rather than german
230 # ngerman, naustrian, german, germanb, austrian
233 # english, USenglish, american, UKenglish, british, canadian
259 def __init__(self
,lang
):
261 # pdflatex does not produce double quotes for ngerman in tt.
262 self
.double_quote_replacment
= None
263 if re
.search('^de',self
.language
):
264 #self.quotes = ("\"`", "\"'")
265 self
.quotes
= ('{\\glqq}', '{\\grqq}')
266 self
.double_quote_replacment
= "{\\dq}"
267 elif re
.search('^it',self
.language
):
268 self
.quotes
= ("``", "''")
269 self
.double_quote_replacment
= r
'{\char`\"}'
271 self
.quotes
= ("``", "''")
274 def next_quote(self
):
275 q
= self
.quotes
[self
.quote_index
]
276 self
.quote_index
= (self
.quote_index
+1)%2
279 def quote_quotes(self
,text
):
281 for part
in text
.split('"'):
285 t
+= self
.next_quote() + part
288 def double_quotes_in_tt (self
,text
):
289 if not self
.double_quote_replacment
:
291 return text
.replace('"', self
.double_quote_replacment
)
293 def get_language(self
):
294 if self
._ISO
639_TO
_BABEL
.has_key(self
.language
):
295 return self
._ISO
639_TO
_BABEL
[self
.language
]
298 l
= self
.language
.split("_")[0]
299 if self
._ISO
639_TO
_BABEL
.has_key(l
):
300 return self
._ISO
639_TO
_BABEL
[l
]
305 'optionlist_environment' : [
306 '\\newcommand{\\optionlistlabel}[1]{\\bf #1 \\hfill}\n'
307 '\\newenvironment{optionlist}[1]\n'
309 ' {\\setlength{\\labelwidth}{#1}\n'
310 ' \\setlength{\\rightmargin}{1cm}\n'
311 ' \\setlength{\\leftmargin}{\\rightmargin}\n'
312 ' \\addtolength{\\leftmargin}{\\labelwidth}\n'
313 ' \\addtolength{\\leftmargin}{\\labelsep}\n'
314 ' \\renewcommand{\\makelabel}{\\optionlistlabel}}\n'
317 'lineblock_environment' : [
318 '\\newlength{\\lineblockindentation}\n'
319 '\\setlength{\\lineblockindentation}{2.5em}\n'
320 '\\newenvironment{lineblock}[1]\n'
322 ' {\\setlength{\\partopsep}{\\parskip}\n'
323 ' \\addtolength{\\partopsep}{\\baselineskip}\n'
324 ' \\topsep0pt\\itemsep0.15\\baselineskip\\parsep0pt\n'
329 'footnote_floats' : [
330 '% begin: floats for footnotes tweaking.\n',
331 '\\setlength{\\floatsep}{0.5em}\n',
332 '\\setlength{\\textfloatsep}{\\fill}\n',
333 '\\addtolength{\\textfloatsep}{3em}\n',
334 '\\renewcommand{\\textfraction}{0.5}\n',
335 '\\renewcommand{\\topfraction}{0.5}\n',
336 '\\renewcommand{\\bottomfraction}{0.5}\n',
337 '\\setcounter{totalnumber}{50}\n',
338 '\\setcounter{topnumber}{50}\n',
339 '\\setcounter{bottomnumber}{50}\n',
340 '% end floats for footnotes\n',
343 '% some commands, that could be overwritten in the style file.\n'
344 '\\newcommand{\\rubric}[1]'
345 '{\\subsection*{~\\hfill {\\it #1} \\hfill ~}}\n'
346 '\\newcommand{\\titlereference}[1]{\\textsl{#1}}\n'
347 '% end of "some commands"\n',
352 """Details of a LaTeX document class."""
354 def __init__(self
, document_class
, with_part
=False):
355 self
.document_class
= document_class
356 self
._with
_part
= with_part
358 def section(self
, level
):
359 """ Return the section name at the given level for the specific
362 Level is 1,2,3..., as level 0 is the title."""
364 sections
= [ 'section', 'subsection', 'subsubsection',
365 'paragraph', 'subparagraph' ]
366 if self
.document_class
in ('book', 'report', 'scrartcl', 'scrbook'):
367 sections
.insert(0, 'chapter')
369 sections
.insert(0, 'part')
370 if level
<= len(sections
):
371 return sections
[level
-1]
376 """ Manage a table while traversing.
377 Maybe change to a mixin defining the visit/departs, but then
378 class Table internal variables are in the Translator.
382 * standard: horizontal and vertical lines
383 * booktabs (requires booktabs latex package): only horizontal lines
384 * nolines, borderless : no lines
386 def __init__(self
,latex_type
,table_style
):
387 self
._latex
_type
= latex_type
388 self
._table
_style
= table_style
390 # miscellaneous attributes
401 self
._in
_head
= 0 # maybe context with search
404 self
._col
_specs
= None
410 def set_table_style(self
, table_style
):
411 if not table_style
in ('standard','booktabs','borderless','nolines'):
413 self
._table
_style
= table_style
415 def used_packages(self
):
416 if self
._table
_style
== 'booktabs':
417 return '\\usepackage{booktabs}\n'
419 def get_latex_type(self
):
420 return self
._latex
_type
422 def set(self
,attr
,value
):
423 self
._attrs
[attr
] = value
425 if self
._attrs
.has_key(attr
):
426 return self
._attrs
[attr
]
428 def get_vertical_bar(self
):
429 if self
._table
_style
== 'standard':
432 # horizontal lines are drawn below a row, because we.
433 def get_opening(self
):
434 if self
._latex
_type
== 'longtable':
435 # otherwise longtable might move before paragraph and subparagraph
436 prefix
= '\\leavevmode\n'
439 return '%s\\begin{%s}[c]' % (prefix
, self
._latex
_type
)
440 def get_closing(self
):
442 if self
._table
_style
== 'booktabs':
443 line
= '\\bottomrule\n'
444 elif self
._table
_style
== 'standard':
446 return '%s\\end{%s}' % (line
,self
._latex
_type
)
448 def visit_colspec(self
, node
):
449 self
._col
_specs
.append(node
)
450 # "stubs" list is an attribute of the tgroup element:
451 self
.stubs
.append(node
.attributes
.get('stub'))
453 def get_colspecs(self
):
455 Return column specification for longtable.
457 Assumes reST line length being 80 characters.
458 Table width is hairy.
464 usually gets to narrow, therefore we add 1 (fiddlefactor).
469 # first see if we get too wide.
470 for node
in self
._col
_specs
:
471 colwidth
= float(node
['colwidth']+1) / width
472 total_width
+= colwidth
475 # donot make it full linewidth
477 if total_width
> 1.0:
478 factor
/= total_width
479 bar
= self
.get_vertical_bar()
480 latex_table_spec
= ""
481 for node
in self
._col
_specs
:
482 colwidth
= factor
* float(node
['colwidth']+1) / width
483 self
._col
_width
.append(colwidth
+0.005)
484 self
._rowspan
.append(0)
485 latex_table_spec
+= "%sp{%.3f\\locallinewidth}" % (bar
,colwidth
+0.005)
486 return latex_table_spec
+bar
488 def get_column_width(self
):
489 """ return columnwidth for current cell (not multicell)
491 return "%.2f\\locallinewidth" % self
._col
_width
[self
._cell
_in
_row
-1]
493 def visit_thead(self
):
495 if self
._table
_style
== 'standard':
497 elif self
._table
_style
== 'booktabs':
498 return ['\\toprule\n']
500 def depart_thead(self
):
502 #if self._table_style == 'standard':
503 # a.append('\\hline\n')
504 if self
._table
_style
== 'booktabs':
505 a
.append('\\midrule\n')
506 if self
._latex
_type
== 'longtable':
507 a
.append('\\endhead\n')
508 # for longtable one could add firsthead, foot and lastfoot
512 self
._cell
_in
_row
= 0
513 def depart_row(self
):
515 self
._cell
_in
_row
= None # remove cell counter
516 for i
in range(len(self
._rowspan
)):
517 if (self
._rowspan
[i
]>0):
518 self
._rowspan
[i
] -= 1
520 if self
._table
_style
== 'standard':
522 for i
in range(len(self
._rowspan
)):
523 if (self
._rowspan
[i
]<=0):
525 if len(rowspans
)==len(self
._rowspan
):
526 res
.append('\\hline\n')
533 c_start
= rowspans
.pop()
536 cline
+= '\\cline{%d-%d}\n' % (c_start
,c_start
)
540 def set_rowspan(self
,cell
,value
):
542 self
._rowspan
[cell
] = value
545 def get_rowspan(self
,cell
):
547 return self
._rowspan
[cell
]
550 def get_entry_number(self
):
551 return self
._cell
_in
_row
552 def visit_entry(self
):
553 self
._cell
_in
_row
+= 1
554 def is_stub_column(self
):
555 if len(self
.stubs
) >= self
._cell
_in
_row
:
556 return self
.stubs
[self
._cell
_in
_row
-1]
560 class LaTeXTranslator(nodes
.NodeVisitor
):
562 # When options are given to the documentclass, latex will pass them
563 # to other packages, as done with babel.
564 # Dummy settings might be taken from document settings
566 latex_head
= '\\documentclass[%s]{%s}\n'
567 linking
= '\\usepackage[colorlinks=%s,linkcolor=%s,urlcolor=%s]{hyperref}\n'
568 stylesheet
= '\\input{%s}\n'
569 # add a generated on day , machine by user using docutils version.
570 generator
= '%% generator Docutils: http://docutils.sourceforge.net/\n'
572 # use latex tableofcontents or let docutils do it.
575 # TODO: use mixins for different implementations.
576 # list environment for docinfo. else tabularx
577 use_optionlist_for_docinfo
= 0 # NOT YET IN USE
579 # Use compound enumerations (1.A.1.)
580 compound_enumerators
= 0
582 # If using compound enumerations, include section information.
583 section_prefix_for_enumerators
= 0
585 # This is the character that separates the section ("." subsection ...)
586 # prefix from the regular list enumerator.
587 section_enumerator_separator
= '-'
590 hyperlink_color
= "blue"
592 def __init__(self
, document
):
593 nodes
.NodeVisitor
.__init
__(self
, document
)
594 self
.settings
= settings
= document
.settings
595 self
.latex_encoding
= self
.to_latex_encoding(settings
.output_encoding
)
596 self
.use_latex_toc
= settings
.use_latex_toc
597 self
.use_latex_docinfo
= settings
.use_latex_docinfo
598 self
.use_latex_footnotes
= settings
.use_latex_footnotes
599 self
._use
_latex
_citations
= settings
.use_latex_citations
600 self
._reference
_label
= settings
.reference_label
601 self
.hyperlink_color
= settings
.hyperlink_color
602 self
.compound_enumerators
= settings
.compound_enumerators
603 self
.font_encoding
= settings
.font_encoding
604 self
.section_prefix_for_enumerators
= (
605 settings
.section_prefix_for_enumerators
)
606 self
.section_enumerator_separator
= (
607 settings
.section_enumerator_separator
.replace('_', '\\_'))
608 if self
.hyperlink_color
== '0':
609 self
.hyperlink_color
= 'black'
610 self
.colorlinks
= 'false'
612 self
.colorlinks
= 'true'
614 if self
.settings
.use_bibtex
:
615 self
.bibtex
= self
.settings
.use_bibtex
.split(",",1)
616 # TODO avoid errors on not declared citations.
619 # language: labels, bibliographic_fields, and author_separators.
620 # to allow writing labes for specific languages.
621 self
.language
= languages
.get_language(settings
.language_code
)
622 self
.babel
= Babel(settings
.language_code
)
623 self
.author_separator
= self
.language
.author_separators
[0]
624 self
.d_options
= self
.settings
.documentoptions
625 if self
.babel
.get_language():
626 self
.d_options
+= ',%s' % \
627 self
.babel
.get_language()
629 self
.d_class
= DocumentClass(settings
.documentclass
, settings
.use_part_section
)
630 # object for a table while proccessing.
631 self
.table_stack
= []
632 self
.active_table
= Table('longtable',settings
.table_style
)
634 # HACK. Should have more sophisticated typearea handling.
635 if settings
.documentclass
.find('scr') == -1:
636 self
.typearea
= '\\usepackage[DIV12]{typearea}\n'
638 if self
.d_options
.find('DIV') == -1 and self
.d_options
.find('BCOR') == -1:
639 self
.typearea
= '\\typearea{12}\n'
643 if self
.font_encoding
== 'OT1':
645 elif self
.font_encoding
== '':
646 fontenc_header
= '\\usepackage{ae}\n\\usepackage{aeguill}\n'
648 fontenc_header
= '\\usepackage[%s]{fontenc}\n' % (self
.font_encoding
,)
649 if self
.latex_encoding
.startswith('utf8'):
650 input_encoding
= '\\usepackage{ucs}\n\\usepackage[utf8x]{inputenc}\n'
652 input_encoding
= '\\usepackage[%s]{inputenc}\n' % self
.latex_encoding
653 if self
.settings
.graphicx_option
== '':
654 self
.graphicx_package
= '\\usepackage{graphicx}\n'
655 elif self
.settings
.graphicx_option
.lower() == 'auto':
656 self
.graphicx_package
= '\n'.join(
657 ('%Check if we are compiling under latex or pdflatex',
658 '\\ifx\\pdftexversion\\undefined',
659 ' \\usepackage{graphicx}',
661 ' \\usepackage[pdftex]{graphicx}',
664 self
.graphicx_package
= (
665 '\\usepackage[%s]{graphicx}\n' % self
.settings
.graphicx_option
)
668 self
.latex_head
% (self
.d_options
,self
.settings
.documentclass
),
669 '\\usepackage{babel}\n', # language is in documents settings.
671 '\\usepackage{shortvrb}\n', # allows verb in footnotes.
673 # * tabularx: for docinfo, automatic width of columns, always on one page.
674 '\\usepackage{tabularx}\n',
675 '\\usepackage{longtable}\n',
676 self
.active_table
.used_packages(),
677 # possible other packages.
679 # * ltxtable is a combination of tabularx and longtable (pagebreaks).
682 # extra space between text in tables and the line above them
683 '\\setlength{\\extrarowheight}{2pt}\n',
684 '\\usepackage{amsmath}\n', # what fore amsmath.
685 self
.graphicx_package
,
686 '\\usepackage{color}\n',
687 '\\usepackage{multirow}\n',
688 '\\usepackage{ifthen}\n', # before hyperref!
689 self
.linking
% (self
.colorlinks
, self
.hyperlink_color
, self
.hyperlink_color
),
693 '\\newlength{\\admonitionwidth}\n',
694 '\\setlength{\\admonitionwidth}{0.9\\textwidth}\n'
695 # width for docinfo tablewidth
696 '\\newlength{\\docinfowidth}\n',
697 '\\setlength{\\docinfowidth}{0.9\\textwidth}\n'
698 # linewidth of current environment, so tables are not wider
699 # than the sidebar: using locallinewidth seems to defer evaluation
700 # of linewidth, this is fixing it.
701 '\\newlength{\\locallinewidth}\n',
704 self
.head_prefix
.extend( latex_headings
['optionlist_environment'] )
705 self
.head_prefix
.extend( latex_headings
['lineblock_environment'] )
706 self
.head_prefix
.extend( latex_headings
['footnote_floats'] )
707 self
.head_prefix
.extend( latex_headings
['some_commands'] )
708 ## stylesheet is last: so it might be possible to overwrite defaults.
709 stylesheet
= utils
.get_stylesheet_reference(settings
)
711 settings
.record_dependencies
.add(stylesheet
)
712 self
.head_prefix
.append(self
.stylesheet
% (stylesheet
))
714 if self
.linking
: # and maybe check for pdf
716 self
.pdfauthor
= None
717 # pdftitle, pdfsubject, pdfauthor, pdfkeywords, pdfcreator, pdfproducer
720 # NOTE: Latex wants a date and an author, rst puts this into
721 # docinfo, so normally we donot want latex author/date handling.
722 # latex article has its own handling of date and author, deactivate.
723 # So we always emit \title{...} \author{...} \date{...}, even if the
724 # "..." are empty strings.
726 # separate title, so we can appen subtitle.
728 # if use_latex_docinfo: collects lists of author/organization/contact/address lines
729 self
.author_stack
= []
732 self
.body_prefix
= ['\\raggedbottom\n']
734 self
.body_suffix
= ['\n']
735 self
.section_level
= 0
737 self
.topic_classes
= []
738 # column specification for tables
739 self
.table_caption
= None
743 # verbatim: to tell encode not to encode.
745 # insert_newline: to tell encode to replace blanks by "~".
746 self
.insert_none_breaking_blanks
= 0
747 # insert_newline: to tell encode to add latex newline.
748 self
.insert_newline
= 0
749 # mbox_newline: to tell encode to add mbox and newline.
750 self
.mbox_newline
= 0
751 # inside citation reference labels underscores dont need to be escaped.
752 self
.inside_citation_reference_label
= 0
754 # Stack of section counters so that we don't have to use_latex_toc.
755 # This will grow and shrink as processing occurs.
756 # Initialized for potential first-level sections.
757 self
._section
_number
= [0]
759 # The current stack of enumerations so that we can expand
760 # them into a compound enumeration.
761 self
._enumeration
_counters
= []
763 # The maximum number of enumeration counters we've used.
764 # If we go beyond this number, we need to create a new
765 # counter; otherwise, just reuse an old one.
766 self
._max
_enumeration
_counters
= 0
772 # inside literal block: no quote mangling.
773 self
.literal_block
= 0
774 self
.literal_block_stack
= []
776 # true when encoding in math mode
779 def to_latex_encoding(self
,docutils_encoding
):
781 Translate docutils encoding name into latex's.
783 Default fallback method is remove "-" and "_" chars from docutils_encoding.
786 tr
= { "iso-8859-1": "latin1", # west european
787 "iso-8859-2": "latin2", # east european
788 "iso-8859-3": "latin3", # esperanto, maltese
789 "iso-8859-4": "latin4", # north european,scandinavian, baltic
790 "iso-8859-5": "iso88595", # cyrillic (ISO)
791 "iso-8859-9": "latin5", # turkish
792 "iso-8859-15": "latin9", # latin9, update to latin1.
793 "mac_cyrillic": "maccyr", # cyrillic (on Mac)
794 "windows-1251": "cp1251", # cyrillic (on Windows)
795 "koi8-r": "koi8-r", # cyrillic (Russian)
796 "koi8-u": "koi8-u", # cyrillic (Ukrainian)
797 "windows-1250": "cp1250", #
798 "windows-1252": "cp1252", #
799 "us-ascii": "ascii", # ASCII (US)
800 # unmatched encodings
802 #"": "ansinew", # windows 3.1 ansi
803 #"": "ascii", # ASCII encoding for the range 32--127.
804 #"": "cp437", # dos latine us
805 #"": "cp850", # dos latin 1
806 #"": "cp852", # dos latin 2
809 #"iso-8859-6": "" # arabic
810 #"iso-8859-7": "" # greek
811 #"iso-8859-8": "" # hebrew
812 #"iso-8859-10": "" # latin6, more complete iso-8859-4
814 if tr
.has_key(docutils_encoding
.lower()):
815 return tr
[docutils_encoding
.lower()]
816 # convert: latin-1 and utf-8 and similar things
817 return docutils_encoding
.replace("_", "").replace("-", "").lower()
819 def language_label(self
, docutil_label
):
820 return self
.language
.labels
[docutil_label
]
822 latex_equivalents
= {
832 u
'\u2020' : '{\\dag}',
833 u
'\u2021' : '{\\ddag}',
834 u
'\u2026' : '{\\dots}',
835 u
'\u2122' : '{\\texttrademark}',
836 u
'\u21d4' : '{$\\Leftrightarrow$}',
840 def unicode_to_latex(self
,text
):
842 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252124
843 # Only some special chracters are translated, for documents with many
844 # utf-8 chars one should use the LaTeX unicode package.
845 for uchar
in self
.latex_equivalents
.keys():
846 text
= text
.replace(uchar
,self
.latex_equivalents
[uchar
])
849 def ensure_math(self
, text
):
850 if not self
.__dict
__.has_key('ensure_math_re'):
852 # lnot,pm,twosuperior,threesuperior,mu,onesuperior,times,div
853 'latin1' : '\xac\xb1\xb2\xb3\xb5\xb9\xd7\xf7' ,
854 # also latin5 and latin9
856 self
.ensure_math_re
= re
.compile('([%s])' % chars
['latin1'])
857 text
= self
.ensure_math_re
.sub(r
'\\ensuremath{\1}', text
)
860 def encode(self
, text
):
862 Encode special characters (``# $ % & ~ _ ^ \ { }``) in `text` & return
864 # Escaping with a backslash does not help with backslashes, ~ and ^.
866 # < > are only available in math-mode or tt font. (really ?)
867 # $ starts math- mode.
871 # compile the regexps once. do it here so one can see them.
874 if not self
.__dict
__.has_key('encode_re_braces'):
875 self
.encode_re_braces
= re
.compile(r
'([{}])')
876 text
= self
.encode_re_braces
.sub(r
'{\\\1}',text
)
877 if not self
.__dict
__.has_key('encode_re_bslash'):
878 # find backslash: except in the form '{\{}' or '{\}}'.
879 self
.encode_re_bslash
= re
.compile(r
'(?<!{)(\\)(?![{}]})')
880 # then the backslash: except in the form from line above:
881 # either '{\{}' or '{\}}'.
882 text
= self
.encode_re_bslash
.sub(r
'{\\textbackslash}', text
)
885 text
= text
.replace("$", '{\\$}')
886 if not ( self
.literal_block
or self
.literal
or self
.mathmode
):
887 # the vertical bar: in mathmode |,\vert or \mid
888 # in textmode \textbar
889 text
= text
.replace("|", '{\\textbar}')
890 text
= text
.replace("<", '{\\textless}')
891 text
= text
.replace(">", '{\\textgreater}')
893 text
= text
.replace("&", '{\\&}')
895 # * verb|^| does not work in mbox.
896 # * mathmode has wedge. hat{~} would also work.
897 # text = text.replace("^", '{\\ensuremath{^\\wedge}}')
898 text
= text
.replace("^", '{\\textasciicircum}')
899 text
= text
.replace("%", '{\\%}')
900 text
= text
.replace("#", '{\\#}')
901 text
= text
.replace("~", '{\\textasciitilde}')
902 # Separate compound characters, e.g. "--" to "-{}-". (The
903 # actual separation is done later; see below.)
905 if self
.literal_block
or self
.literal
:
906 # In monospace-font, we also separate ",,", "``" and "''"
907 # and some other characters which can't occur in
909 separate_chars
+= ',`\'"<>'
910 # pdflatex does not produce doublequotes for ngerman.
911 text
= self
.babel
.double_quotes_in_tt(text
)
912 if self
.font_encoding
== 'OT1':
913 # We're using OT1 font-encoding and have to replace
914 # underscore by underlined blank, because this has
916 text
= text
.replace('_', '{\\underline{ }}')
917 # And the tt-backslash doesn't work in OT1, so we use
919 text
= text
.replace('\\textbackslash', '\\reflectbox{/}')
921 text
= text
.replace('_', '{\\_}')
923 text
= self
.babel
.quote_quotes(text
)
924 if not self
.inside_citation_reference_label
:
925 text
= text
.replace("_", '{\\_}')
926 for char
in separate_chars
* 2:
927 # Do it twice ("* 2") becaues otherwise we would replace
929 text
= text
.replace(char
+ char
, char
+ '{}' + char
)
930 if self
.insert_newline
or self
.literal_block
:
931 # Insert a blank before the newline, to avoid
932 # ! LaTeX Error: There's no line here to end.
933 text
= text
.replace("\n", '~\\\\\n')
934 elif self
.mbox_newline
:
935 if self
.literal_block
:
936 closings
= "}" * len(self
.literal_block_stack
)
937 openings
= "".join(self
.literal_block_stack
)
941 text
= text
.replace("\n", "%s}\\\\\n\\mbox{%s" % (closings
,openings
))
942 text
= text
.replace('[', '{[}').replace(']', '{]}')
943 if self
.insert_none_breaking_blanks
:
944 text
= text
.replace(' ', '~')
945 if self
.latex_encoding
!= 'utf8':
946 text
= self
.unicode_to_latex(text
)
947 text
= self
.ensure_math(text
)
950 def attval(self
, text
,
951 whitespace
=re
.compile('[\n\r\t\v\f]')):
952 """Cleanse, encode, and return attribute value text."""
953 return self
.encode(whitespace
.sub(' ', text
))
956 if self
.pdfinfo
is not None:
958 self
.pdfinfo
.append('pdfauthor={%s}' % self
.pdfauthor
)
960 pdfinfo
= '\\hypersetup{\n' + ',\n'.join(self
.pdfinfo
) + '\n}\n'
963 head
= '\\title{%s}\n\\author{%s}\n\\date{%s}\n' % \
965 ' \\and\n'.join(['~\\\\\n'.join(author_lines
)
966 for author_lines
in self
.author_stack
]),
968 return ''.join(self
.head_prefix
+ [head
] + self
.head
+ [pdfinfo
]
969 + self
.body_prefix
+ self
.body
+ self
.body_suffix
)
971 def visit_Text(self
, node
):
972 self
.body
.append(self
.encode(node
.astext()))
974 def depart_Text(self
, node
):
977 def visit_address(self
, node
):
978 self
.visit_docinfo_item(node
, 'address')
980 def depart_address(self
, node
):
981 self
.depart_docinfo_item(node
)
983 def visit_admonition(self
, node
, name
=''):
984 self
.body
.append('\\begin{center}\\begin{sffamily}\n')
985 self
.body
.append('\\fbox{\\parbox{\\admonitionwidth}{\n')
987 self
.body
.append('\\textbf{\\large '+ self
.language
.labels
[name
] + '}\n');
988 self
.body
.append('\\vspace{2mm}\n')
991 def depart_admonition(self
, node
=None):
992 self
.body
.append('}}\n') # end parbox fbox
993 self
.body
.append('\\end{sffamily}\n\\end{center}\n');
995 def visit_attention(self
, node
):
996 self
.visit_admonition(node
, 'attention')
998 def depart_attention(self
, node
):
999 self
.depart_admonition()
1001 def visit_author(self
, node
):
1002 self
.visit_docinfo_item(node
, 'author')
1004 def depart_author(self
, node
):
1005 self
.depart_docinfo_item(node
)
1007 def visit_authors(self
, node
):
1008 # not used: visit_author is called anyway for each author.
1011 def depart_authors(self
, node
):
1014 def visit_block_quote(self
, node
):
1015 self
.body
.append( '\\begin{quote}\n')
1017 def depart_block_quote(self
, node
):
1018 self
.body
.append( '\\end{quote}\n')
1020 def visit_bullet_list(self
, node
):
1021 if 'contents' in self
.topic_classes
:
1022 if self
.use_latex_toc
:
1023 raise nodes
.SkipNode
1024 self
.body
.append( '\\begin{list}{}{}\n' )
1026 self
.body
.append( '\\begin{itemize}\n' )
1028 def depart_bullet_list(self
, node
):
1029 if 'contents' in self
.topic_classes
:
1030 self
.body
.append( '\\end{list}\n' )
1032 self
.body
.append( '\\end{itemize}\n' )
1034 # Imperfect superscript/subscript handling: mathmode italicizes
1035 # all letters by default.
1036 def visit_superscript(self
, node
):
1037 self
.body
.append('$^{')
1040 def depart_superscript(self
, node
):
1041 self
.body
.append('}$')
1044 def visit_subscript(self
, node
):
1045 self
.body
.append('$_{')
1048 def depart_subscript(self
, node
):
1049 self
.body
.append('}$')
1052 def visit_caption(self
, node
):
1053 self
.body
.append( '\\caption{' )
1055 def depart_caption(self
, node
):
1056 self
.body
.append('}')
1058 def visit_caution(self
, node
):
1059 self
.visit_admonition(node
, 'caution')
1061 def depart_caution(self
, node
):
1062 self
.depart_admonition()
1064 def visit_title_reference(self
, node
):
1065 self
.body
.append( '\\titlereference{' )
1067 def depart_title_reference(self
, node
):
1068 self
.body
.append( '}' )
1070 def visit_citation(self
, node
):
1071 # TODO maybe use cite bibitems
1072 if self
._use
_latex
_citations
:
1073 self
.context
.append(len(self
.body
))
1075 self
.body
.append('\\begin{figure}[b]')
1076 for id in node
['ids']:
1077 self
.body
.append('\\hypertarget{%s}' % id)
1079 def depart_citation(self
, node
):
1080 if self
._use
_latex
_citations
:
1081 size
= self
.context
.pop()
1082 label
= self
.body
[size
]
1083 text
= ''.join(self
.body
[size
+1:])
1084 del self
.body
[size
:]
1085 self
._bibitems
.append([label
, text
])
1087 self
.body
.append('\\end{figure}\n')
1089 def visit_citation_reference(self
, node
):
1090 if self
._use
_latex
_citations
:
1091 self
.body
.append('\\cite{')
1092 self
.inside_citation_reference_label
= 1
1095 if node
.has_key('refid'):
1096 href
= node
['refid']
1097 elif node
.has_key('refname'):
1098 href
= self
.document
.nameids
[node
['refname']]
1099 self
.body
.append('[\\hyperlink{%s}{' % href
)
1101 def depart_citation_reference(self
, node
):
1102 if self
._use
_latex
_citations
:
1103 self
.body
.append('}')
1104 self
.inside_citation_reference_label
= 0
1106 self
.body
.append('}]')
1108 def visit_classifier(self
, node
):
1109 self
.body
.append( '(\\textbf{' )
1111 def depart_classifier(self
, node
):
1112 self
.body
.append( '})\n' )
1114 def visit_colspec(self
, node
):
1115 self
.active_table
.visit_colspec(node
)
1117 def depart_colspec(self
, node
):
1120 def visit_comment(self
, node
):
1121 # Escape end of line by a new comment start in comment text.
1122 self
.body
.append('%% %s \n' % node
.astext().replace('\n', '\n% '))
1123 raise nodes
.SkipNode
1125 def visit_compound(self
, node
):
1128 def depart_compound(self
, node
):
1131 def visit_contact(self
, node
):
1132 self
.visit_docinfo_item(node
, 'contact')
1134 def depart_contact(self
, node
):
1135 self
.depart_docinfo_item(node
)
1137 def visit_container(self
, node
):
1140 def depart_container(self
, node
):
1143 def visit_copyright(self
, node
):
1144 self
.visit_docinfo_item(node
, 'copyright')
1146 def depart_copyright(self
, node
):
1147 self
.depart_docinfo_item(node
)
1149 def visit_danger(self
, node
):
1150 self
.visit_admonition(node
, 'danger')
1152 def depart_danger(self
, node
):
1153 self
.depart_admonition()
1155 def visit_date(self
, node
):
1156 self
.visit_docinfo_item(node
, 'date')
1158 def depart_date(self
, node
):
1159 self
.depart_docinfo_item(node
)
1161 def visit_decoration(self
, node
):
1164 def depart_decoration(self
, node
):
1167 def visit_definition(self
, node
):
1170 def depart_definition(self
, node
):
1171 self
.body
.append('\n')
1173 def visit_definition_list(self
, node
):
1174 self
.body
.append( '\\begin{description}\n' )
1176 def depart_definition_list(self
, node
):
1177 self
.body
.append( '\\end{description}\n' )
1179 def visit_definition_list_item(self
, node
):
1182 def depart_definition_list_item(self
, node
):
1185 def visit_description(self
, node
):
1186 self
.body
.append( ' ' )
1188 def depart_description(self
, node
):
1191 def visit_docinfo(self
, node
):
1193 self
.docinfo
.append('%' + '_'*75 + '\n')
1194 self
.docinfo
.append('\\begin{center}\n')
1195 self
.docinfo
.append('\\begin{tabularx}{\\docinfowidth}{lX}\n')
1197 def depart_docinfo(self
, node
):
1198 self
.docinfo
.append('\\end{tabularx}\n')
1199 self
.docinfo
.append('\\end{center}\n')
1200 self
.body
= self
.docinfo
+ self
.body
1201 # clear docinfo, so field names are no longer appended.
1204 def visit_docinfo_item(self
, node
, name
):
1205 if name
== 'author':
1206 if not self
.pdfinfo
== None:
1207 if not self
.pdfauthor
:
1208 self
.pdfauthor
= self
.attval(node
.astext())
1210 self
.pdfauthor
+= self
.author_separator
+ self
.attval(node
.astext())
1211 if self
.use_latex_docinfo
:
1212 if name
in ('author', 'organization', 'contact', 'address'):
1213 # We attach these to the last author. If any of them precedes
1214 # the first author, put them in a separate "author" group (for
1215 # no better semantics).
1216 if name
== 'author' or not self
.author_stack
:
1217 self
.author_stack
.append([])
1218 if name
== 'address': # newlines are meaningful
1219 self
.insert_newline
= 1
1220 text
= self
.encode(node
.astext())
1221 self
.insert_newline
= 0
1223 text
= self
.attval(node
.astext())
1224 self
.author_stack
[-1].append(text
)
1225 raise nodes
.SkipNode
1226 elif name
== 'date':
1227 self
.date
= self
.attval(node
.astext())
1228 raise nodes
.SkipNode
1229 self
.docinfo
.append('\\textbf{%s}: &\n\t' % self
.language_label(name
))
1230 if name
== 'address':
1231 self
.insert_newline
= 1
1232 self
.docinfo
.append('{\\raggedright\n')
1233 self
.context
.append(' } \\\\\n')
1235 self
.context
.append(' \\\\\n')
1236 self
.context
.append(self
.docinfo
)
1237 self
.context
.append(len(self
.body
))
1239 def depart_docinfo_item(self
, node
):
1240 size
= self
.context
.pop()
1241 dest
= self
.context
.pop()
1242 tail
= self
.context
.pop()
1243 tail
= self
.body
[size
:] + [tail
]
1244 del self
.body
[size
:]
1246 # for address we did set insert_newline
1247 self
.insert_newline
= 0
1249 def visit_doctest_block(self
, node
):
1250 self
.body
.append( '\\begin{verbatim}' )
1253 def depart_doctest_block(self
, node
):
1254 self
.body
.append( '\\end{verbatim}\n' )
1257 def visit_document(self
, node
):
1258 self
.body_prefix
.append('\\begin{document}\n')
1260 if self
.use_latex_docinfo
or len(node
) and isinstance(node
[0], nodes
.title
):
1261 self
.body_prefix
.append('\\maketitle\n\n')
1262 # alternative use titlepage environment.
1264 self
.body
.append('\n\\setlength{\\locallinewidth}{\\linewidth}\n')
1266 def depart_document(self
, node
):
1267 # TODO insertion point of bibliography should none automatic.
1268 if self
._use
_latex
_citations
and len(self
._bibitems
)>0:
1271 for bi
in self
._bibitems
:
1272 if len(widest_label
)<len(bi
[0]):
1273 widest_label
= bi
[0]
1274 self
.body
.append('\n\\begin{thebibliography}{%s}\n'%widest_label
)
1275 for bi
in self
._bibitems
:
1276 # cite_key: underscores must not be escaped
1277 cite_key
= bi
[0].replace(r
"{\_}","_")
1278 self
.body
.append('\\bibitem[%s]{%s}{%s}\n' % (bi
[0], cite_key
, bi
[1]))
1279 self
.body
.append('\\end{thebibliography}\n')
1281 self
.body
.append('\n\\bibliographystyle{%s}\n' % self
.bibtex
[0])
1282 self
.body
.append('\\bibliography{%s}\n' % self
.bibtex
[1])
1284 self
.body_suffix
.append('\\end{document}\n')
1286 def visit_emphasis(self
, node
):
1287 self
.body
.append('\\emph{')
1288 self
.literal_block_stack
.append('\\emph{')
1290 def depart_emphasis(self
, node
):
1291 self
.body
.append('}')
1292 self
.literal_block_stack
.pop()
1294 def visit_entry(self
, node
):
1295 self
.active_table
.visit_entry()
1297 if self
.active_table
.get_entry_number() == 1:
1298 # if the firstrow is a multirow, this actually is the second row.
1299 # this gets hairy if rowspans follow each other.
1300 if self
.active_table
.get_rowspan(0):
1302 while self
.active_table
.get_rowspan(count
):
1304 self
.body
.append(' & ')
1305 self
.active_table
.visit_entry() # increment cell count
1307 self
.body
.append(' & ')
1310 # IN WORK BUG TODO HACK continues here
1311 # multirow in LaTeX simply will enlarge the cell over several rows
1312 # (the following n if n is positive, the former if negative).
1313 if node
.has_key('morerows') and node
.has_key('morecols'):
1314 raise NotImplementedError('Cells that '
1315 'span multiple rows *and* columns are not supported, sorry.')
1316 if node
.has_key('morerows'):
1317 count
= node
['morerows'] + 1
1318 self
.active_table
.set_rowspan(self
.active_table
.get_entry_number()-1,count
)
1319 self
.body
.append('\\multirow{%d}{%s}{' % \
1320 (count
,self
.active_table
.get_column_width()))
1321 self
.context
.append('}')
1322 # BUG following rows must have empty cells.
1323 elif node
.has_key('morecols'):
1324 # the vertical bar before column is missing if it is the first column.
1325 # the one after always.
1326 if self
.active_table
.get_entry_number() == 1:
1327 bar1
= self
.active_table
.get_vertical_bar()
1330 count
= node
['morecols'] + 1
1331 self
.body
.append('\\multicolumn{%d}{%sl%s}{' % \
1332 (count
, bar1
, self
.active_table
.get_vertical_bar()))
1333 self
.context
.append('}')
1335 self
.context
.append('')
1337 # header / not header
1338 if isinstance(node
.parent
.parent
, nodes
.thead
):
1339 self
.body
.append('\\textbf{')
1340 self
.context
.append('}')
1341 elif self
.active_table
.is_stub_column():
1342 self
.body
.append('\\textbf{')
1343 self
.context
.append('}')
1345 self
.context
.append('')
1347 def depart_entry(self
, node
):
1348 self
.body
.append(self
.context
.pop()) # header / not header
1349 self
.body
.append(self
.context
.pop()) # multirow/column
1350 # if following row is spanned from above.
1351 if self
.active_table
.get_rowspan(self
.active_table
.get_entry_number()):
1352 self
.body
.append(' & ')
1353 self
.active_table
.visit_entry() # increment cell count
1355 def visit_row(self
, node
):
1356 self
.active_table
.visit_row()
1358 def depart_row(self
, node
):
1359 self
.body
.extend(self
.active_table
.depart_row())
1361 def visit_enumerated_list(self
, node
):
1362 # We create our own enumeration list environment.
1363 # This allows to set the style and starting value
1364 # and unlimited nesting.
1365 enum_style
= {'arabic':'arabic',
1366 'loweralpha':'alph',
1367 'upperalpha':'Alph',
1368 'lowerroman':'roman',
1369 'upperroman':'Roman' }
1371 if node
.has_key('suffix'):
1372 enum_suffix
= node
['suffix']
1374 if node
.has_key('prefix'):
1375 enum_prefix
= node
['prefix']
1376 if self
.compound_enumerators
:
1378 if self
.section_prefix_for_enumerators
and self
.section_level
:
1379 for i
in range(self
.section_level
):
1380 pref
+= '%d.' % self
._section
_number
[i
]
1381 pref
= pref
[:-1] + self
.section_enumerator_separator
1383 for ctype
, cname
in self
._enumeration
_counters
:
1384 enum_prefix
+= '\\%s{%s}.' % (ctype
, cname
)
1385 enum_type
= "arabic"
1386 if node
.has_key('enumtype'):
1387 enum_type
= node
['enumtype']
1388 if enum_style
.has_key(enum_type
):
1389 enum_type
= enum_style
[enum_type
]
1391 counter_name
= "listcnt%d" % len(self
._enumeration
_counters
)
1392 self
._enumeration
_counters
.append((enum_type
, counter_name
))
1393 # If we haven't used this counter name before, then create a
1394 # new counter; otherwise, reset & reuse the old counter.
1395 if len(self
._enumeration
_counters
) > self
._max
_enumeration
_counters
:
1396 self
._max
_enumeration
_counters
= len(self
._enumeration
_counters
)
1397 self
.body
.append('\\newcounter{%s}\n' % counter_name
)
1399 self
.body
.append('\\setcounter{%s}{0}\n' % counter_name
)
1401 self
.body
.append('\\begin{list}{%s\\%s{%s}%s}\n' % \
1402 (enum_prefix
,enum_type
,counter_name
,enum_suffix
))
1403 self
.body
.append('{\n')
1404 self
.body
.append('\\usecounter{%s}\n' % counter_name
)
1405 # set start after usecounter, because it initializes to zero.
1406 if node
.has_key('start'):
1407 self
.body
.append('\\addtocounter{%s}{%d}\n' \
1408 % (counter_name
,node
['start']-1))
1409 ## set rightmargin equal to leftmargin
1410 self
.body
.append('\\setlength{\\rightmargin}{\\leftmargin}\n')
1411 self
.body
.append('}\n')
1413 def depart_enumerated_list(self
, node
):
1414 self
.body
.append('\\end{list}\n')
1415 self
._enumeration
_counters
.pop()
1417 def visit_error(self
, node
):
1418 self
.visit_admonition(node
, 'error')
1420 def depart_error(self
, node
):
1421 self
.depart_admonition()
1423 def visit_field(self
, node
):
1424 # real output is done in siblings: _argument, _body, _name
1427 def depart_field(self
, node
):
1428 self
.body
.append('\n')
1429 ##self.body.append('%[depart_field]\n')
1431 def visit_field_argument(self
, node
):
1432 self
.body
.append('%[visit_field_argument]\n')
1434 def depart_field_argument(self
, node
):
1435 self
.body
.append('%[depart_field_argument]\n')
1437 def visit_field_body(self
, node
):
1438 # BUG by attach as text we loose references.
1440 self
.docinfo
.append('%s \\\\\n' % self
.encode(node
.astext()))
1441 raise nodes
.SkipNode
1442 # BUG: what happens if not docinfo
1444 def depart_field_body(self
, node
):
1445 self
.body
.append( '\n' )
1447 def visit_field_list(self
, node
):
1448 if not self
.docinfo
:
1449 self
.body
.append('\\begin{quote}\n')
1450 self
.body
.append('\\begin{description}\n')
1452 def depart_field_list(self
, node
):
1453 if not self
.docinfo
:
1454 self
.body
.append('\\end{description}\n')
1455 self
.body
.append('\\end{quote}\n')
1457 def visit_field_name(self
, node
):
1458 # BUG this duplicates docinfo_item
1460 self
.docinfo
.append('\\textbf{%s}: &\n\t' % self
.encode(node
.astext()))
1461 raise nodes
.SkipNode
1463 self
.body
.append('\\item [')
1465 def depart_field_name(self
, node
):
1466 if not self
.docinfo
:
1467 self
.body
.append(':]')
1469 def visit_figure(self
, node
):
1470 if (not node
.attributes
.has_key('align') or
1471 node
.attributes
['align'] == 'center'):
1472 # centering does not add vertical space like center.
1473 align
= '\n\\centering'
1476 # TODO non vertical space for other alignments.
1477 align
= '\\begin{flush%s}' % node
.attributes
['align']
1478 align_end
= '\\end{flush%s}' % node
.attributes
['align']
1479 self
.body
.append( '\\begin{figure}[htbp]%s\n' % align
)
1480 self
.context
.append( '%s\\end{figure}\n' % align_end
)
1482 def depart_figure(self
, node
):
1483 self
.body
.append( self
.context
.pop() )
1485 def visit_footer(self
, node
):
1486 self
.context
.append(len(self
.body
))
1488 def depart_footer(self
, node
):
1489 start
= self
.context
.pop()
1490 footer
= (['\n\\begin{center}\small\n']
1491 + self
.body
[start
:] + ['\n\\end{center}\n'])
1492 self
.body_suffix
[:0] = footer
1493 del self
.body
[start
:]
1495 def visit_footnote(self
, node
):
1496 if self
.use_latex_footnotes
:
1497 num
,text
= node
.astext().split(None,1)
1498 num
= self
.encode(num
.strip())
1499 self
.body
.append('\\footnotetext['+num
+']')
1500 self
.body
.append('{')
1502 self
.body
.append('\\begin{figure}[b]')
1503 for id in node
['ids']:
1504 self
.body
.append('\\hypertarget{%s}' % id)
1506 def depart_footnote(self
, node
):
1507 if self
.use_latex_footnotes
:
1508 self
.body
.append('}\n')
1510 self
.body
.append('\\end{figure}\n')
1512 def visit_footnote_reference(self
, node
):
1513 if self
.use_latex_footnotes
:
1514 self
.body
.append("\\footnotemark["+self
.encode(node
.astext())+"]")
1515 raise nodes
.SkipNode
1517 if node
.has_key('refid'):
1518 href
= node
['refid']
1519 elif node
.has_key('refname'):
1520 href
= self
.document
.nameids
[node
['refname']]
1521 format
= self
.settings
.footnote_references
1522 if format
== 'brackets':
1524 self
.context
.append(']')
1525 elif format
== 'superscript':
1526 suffix
= '\\raisebox{.5em}[0em]{\\scriptsize'
1527 self
.context
.append('}')
1528 else: # shouldn't happen
1529 raise AssertionError('Illegal footnote reference format.')
1530 self
.body
.append('%s\\hyperlink{%s}{' % (suffix
,href
))
1532 def depart_footnote_reference(self
, node
):
1533 if self
.use_latex_footnotes
:
1535 self
.body
.append('}%s' % self
.context
.pop())
1537 # footnote/citation label
1538 def label_delim(self
, node
, bracket
, superscript
):
1539 if isinstance(node
.parent
, nodes
.footnote
):
1540 if self
.use_latex_footnotes
:
1541 raise nodes
.SkipNode
1542 if self
.settings
.footnote_references
== 'brackets':
1543 self
.body
.append(bracket
)
1545 self
.body
.append(superscript
)
1547 assert isinstance(node
.parent
, nodes
.citation
)
1548 if not self
._use
_latex
_citations
:
1549 self
.body
.append(bracket
)
1551 def visit_label(self
, node
):
1552 self
.label_delim(node
, '[', '$^{')
1554 def depart_label(self
, node
):
1555 self
.label_delim(node
, ']', '}$')
1557 # elements generated by the framework e.g. section numbers.
1558 def visit_generated(self
, node
):
1561 def depart_generated(self
, node
):
1564 def visit_header(self
, node
):
1565 self
.context
.append(len(self
.body
))
1567 def depart_header(self
, node
):
1568 start
= self
.context
.pop()
1569 self
.body_prefix
.append('\n\\verb|begin_header|\n')
1570 self
.body_prefix
.extend(self
.body
[start
:])
1571 self
.body_prefix
.append('\n\\verb|end_header|\n')
1572 del self
.body
[start
:]
1574 def visit_hint(self
, node
):
1575 self
.visit_admonition(node
, 'hint')
1577 def depart_hint(self
, node
):
1578 self
.depart_admonition()
1580 def latex_image_length(self
, width_str
):
1581 match
= re
.match('(\d*\.?\d*)\s*(\S*)', width_str
)
1586 amount
, unit
= match
.groups()[:2]
1588 # LaTeX does not know pixels but points
1589 res
= "%spt" % amount
1591 res
= "%.3f\\linewidth" % (float(amount
)/100.0)
1594 def visit_image(self
, node
):
1595 attrs
= node
.attributes
1596 # Add image URI to dependency list, assuming that it's
1597 # referring to a local file.
1598 self
.settings
.record_dependencies
.add(attrs
['uri'])
1599 pre
= [] # in reverse order
1601 include_graphics_options
= []
1602 inline
= isinstance(node
.parent
, nodes
.TextElement
)
1603 if attrs
.has_key('scale'):
1604 # Could also be done with ``scale`` option to
1605 # ``\includegraphics``; doing it this way for consistency.
1606 pre
.append('\\scalebox{%f}{' % (attrs
['scale'] / 100.0,))
1608 if attrs
.has_key('width'):
1609 include_graphics_options
.append('width=%s' % (
1610 self
.latex_image_length(attrs
['width']), ))
1611 if attrs
.has_key('height'):
1612 include_graphics_options
.append('height=%s' % (
1613 self
.latex_image_length(attrs
['height']), ))
1614 if attrs
.has_key('align'):
1616 # By default latex aligns the top of an image.
1617 (1, 'top'): ('', ''),
1618 (1, 'middle'): ('\\raisebox{-0.5\\height}{', '}'),
1619 (1, 'bottom'): ('\\raisebox{-\\height}{', '}'),
1620 (0, 'center'): ('{\\hfill', '\\hfill}'),
1621 # These 2 don't exactly do the right thing. The image should
1622 # be floated alongside the paragraph. See
1623 # http://www.w3.org/TR/html4/struct/objects.html#adef-align-IMG
1624 (0, 'left'): ('{', '\\hfill}'),
1625 (0, 'right'): ('{\\hfill', '}'),}
1627 pre
.append(align_prepost
[inline
, attrs
['align']][0])
1628 post
.append(align_prepost
[inline
, attrs
['align']][1])
1630 pass # XXX complain here?
1635 self
.body
.extend( pre
)
1637 if len(include_graphics_options
)>0:
1638 options
= '[%s]' % (','.join(include_graphics_options
))
1639 self
.body
.append( '\\includegraphics%s{%s}' % (
1640 options
, attrs
['uri'] ) )
1641 self
.body
.extend( post
)
1643 def depart_image(self
, node
):
1646 def visit_important(self
, node
):
1647 self
.visit_admonition(node
, 'important')
1649 def depart_important(self
, node
):
1650 self
.depart_admonition()
1652 def visit_interpreted(self
, node
):
1653 # @@@ Incomplete, pending a proper implementation on the
1654 # Parser/Reader end.
1655 self
.visit_literal(node
)
1657 def depart_interpreted(self
, node
):
1658 self
.depart_literal(node
)
1660 def visit_legend(self
, node
):
1661 self
.body
.append('{\\small ')
1663 def depart_legend(self
, node
):
1664 self
.body
.append('}')
1666 def visit_line(self
, node
):
1667 self
.body
.append('\item[] ')
1669 def depart_line(self
, node
):
1670 self
.body
.append('\n')
1672 def visit_line_block(self
, node
):
1673 if isinstance(node
.parent
, nodes
.line_block
):
1674 self
.body
.append('\\item[] \n'
1675 '\\begin{lineblock}{\\lineblockindentation}\n')
1677 self
.body
.append('\n\\begin{lineblock}{0em}\n')
1679 def depart_line_block(self
, node
):
1680 self
.body
.append('\\end{lineblock}\n')
1682 def visit_list_item(self
, node
):
1683 # Append "{}" in case the next character is "[", which would break
1684 # LaTeX's list environment (no numbering and the "[" is not printed).
1685 self
.body
.append('\\item {} ')
1687 def depart_list_item(self
, node
):
1688 self
.body
.append('\n')
1690 def visit_literal(self
, node
):
1692 self
.body
.append('\\texttt{')
1694 def depart_literal(self
, node
):
1695 self
.body
.append('}')
1698 def visit_literal_block(self
, node
):
1700 Render a literal-block.
1702 Literal blocks are used for "::"-prefixed literal-indented
1703 blocks of text, where the inline markup is not recognized,
1704 but are also the product of the parsed-literal directive,
1705 where the markup is respected.
1707 # In both cases, we want to use a typewriter/monospaced typeface.
1708 # For "real" literal-blocks, we can use \verbatim, while for all
1709 # the others we must use \mbox.
1711 # We can distinguish between the two kinds by the number of
1712 # siblings that compose this node: if it is composed by a
1713 # single element, it's surely either a real one or a
1714 # parsed-literal that does not contain any markup.
1716 if not self
.active_table
.is_open():
1717 # no quote inside tables, to avoid vertical space between
1718 # table border and literal block.
1719 # BUG: fails if normal text preceeds the literal block.
1720 self
.body
.append('\\begin{quote}')
1721 self
.context
.append('\\end{quote}\n')
1723 self
.body
.append('\n')
1724 self
.context
.append('\n')
1725 if (self
.settings
.use_verbatim_when_possible
and (len(node
) == 1)
1726 # in case of a parsed-literal containing just a "**bold**" word:
1727 and isinstance(node
[0], nodes
.Text
)):
1729 self
.body
.append('\\begin{verbatim}\n')
1731 self
.literal_block
= 1
1732 self
.insert_none_breaking_blanks
= 1
1733 self
.body
.append('{\\ttfamily \\raggedright \\noindent\n')
1734 # * obey..: is from julien and never worked for me (grubert).
1735 # self.body.append('{\\obeylines\\obeyspaces\\ttfamily\n')
1737 def depart_literal_block(self
, node
):
1739 self
.body
.append('\n\\end{verbatim}')
1742 self
.body
.append('\n}')
1743 self
.insert_none_breaking_blanks
= 0
1744 self
.literal_block
= 0
1745 # obey end: self.body.append('}\n')
1746 self
.body
.append(self
.context
.pop())
1748 def visit_meta(self
, node
):
1749 self
.body
.append('[visit_meta]\n')
1750 # BUG maybe set keywords for pdf
1751 ##self.head.append(self.starttag(node, 'meta', **node.attributes))
1753 def depart_meta(self
, node
):
1754 self
.body
.append('[depart_meta]\n')
1756 def visit_note(self
, node
):
1757 self
.visit_admonition(node
, 'note')
1759 def depart_note(self
, node
):
1760 self
.depart_admonition()
1762 def visit_option(self
, node
):
1763 if self
.context
[-1]:
1764 # this is not the first option
1765 self
.body
.append(', ')
1767 def depart_option(self
, node
):
1768 # flag tha the first option is done.
1769 self
.context
[-1] += 1
1771 def visit_option_argument(self
, node
):
1772 """The delimiter betweeen an option and its argument."""
1773 self
.body
.append(node
.get('delimiter', ' '))
1775 def depart_option_argument(self
, node
):
1778 def visit_option_group(self
, node
):
1779 self
.body
.append('\\item [')
1780 # flag for first option
1781 self
.context
.append(0)
1783 def depart_option_group(self
, node
):
1784 self
.context
.pop() # the flag
1785 self
.body
.append('] ')
1787 def visit_option_list(self
, node
):
1788 self
.body
.append('\\begin{optionlist}{3cm}\n')
1790 def depart_option_list(self
, node
):
1791 self
.body
.append('\\end{optionlist}\n')
1793 def visit_option_list_item(self
, node
):
1796 def depart_option_list_item(self
, node
):
1799 def visit_option_string(self
, node
):
1800 ##self.body.append(self.starttag(node, 'span', '', CLASS='option'))
1803 def depart_option_string(self
, node
):
1804 ##self.body.append('</span>')
1807 def visit_organization(self
, node
):
1808 self
.visit_docinfo_item(node
, 'organization')
1810 def depart_organization(self
, node
):
1811 self
.depart_docinfo_item(node
)
1813 def visit_paragraph(self
, node
):
1814 index
= node
.parent
.index(node
)
1815 if not ('contents' in self
.topic_classes
or
1816 (isinstance(node
.parent
, nodes
.compound
) and
1818 not isinstance(node
.parent
[index
- 1], nodes
.paragraph
) and
1819 not isinstance(node
.parent
[index
- 1], nodes
.compound
))):
1820 self
.body
.append('\n')
1822 def depart_paragraph(self
, node
):
1823 self
.body
.append('\n')
1825 def visit_problematic(self
, node
):
1826 self
.body
.append('{\\color{red}\\bfseries{}')
1828 def depart_problematic(self
, node
):
1829 self
.body
.append('}')
1831 def visit_raw(self
, node
):
1832 if 'latex' in node
.get('format', '').split():
1833 self
.body
.append(node
.astext())
1834 raise nodes
.SkipNode
1836 def visit_reference(self
, node
):
1837 # BUG: hash_char "#" is trouble some in LaTeX.
1838 # mbox and other environment do not like the '#'.
1840 if node
.has_key('refuri'):
1841 href
= node
['refuri'].replace('#',hash_char
)
1842 elif node
.has_key('refid'):
1843 href
= hash_char
+ node
['refid']
1844 elif node
.has_key('refname'):
1845 href
= hash_char
+ self
.document
.nameids
[node
['refname']]
1847 raise AssertionError('Unknown reference.')
1848 self
.body
.append('\\href{%s}{' % href
)
1849 if self
._reference
_label
and not node
.has_key('refuri'):
1850 self
.body
.append('\\%s{%s}}' % (self
._reference
_label
,
1851 href
.replace(hash_char
, '')))
1852 raise nodes
.SkipNode
1854 def depart_reference(self
, node
):
1855 self
.body
.append('}')
1857 def visit_revision(self
, node
):
1858 self
.visit_docinfo_item(node
, 'revision')
1860 def depart_revision(self
, node
):
1861 self
.depart_docinfo_item(node
)
1863 def visit_section(self
, node
):
1864 self
.section_level
+= 1
1865 # Initialize counter for potential subsections:
1866 self
._section
_number
.append(0)
1867 # Counter for this section's level (initialized by parent section):
1868 self
._section
_number
[self
.section_level
- 1] += 1
1870 def depart_section(self
, node
):
1871 # Remove counter for potential subsections:
1872 self
._section
_number
.pop()
1873 self
.section_level
-= 1
1875 def visit_sidebar(self
, node
):
1876 # BUG: this is just a hack to make sidebars render something
1877 self
.body
.append('\n\\setlength{\\locallinewidth}{0.9\\admonitionwidth}\n')
1878 self
.body
.append('\\begin{center}\\begin{sffamily}\n')
1879 self
.body
.append('\\fbox{\\colorbox[gray]{0.80}{\\parbox{\\admonitionwidth}{\n')
1881 def depart_sidebar(self
, node
):
1882 self
.body
.append('}}}\n') # end parbox colorbox fbox
1883 self
.body
.append('\\end{sffamily}\n\\end{center}\n');
1884 self
.body
.append('\n\\setlength{\\locallinewidth}{\\linewidth}\n')
1887 attribution_formats
= {'dash': ('---', ''),
1888 'parentheses': ('(', ')'),
1889 'parens': ('(', ')'),
1892 def visit_attribution(self
, node
):
1893 prefix
, suffix
= self
.attribution_formats
[self
.settings
.attribution
]
1894 self
.body
.append('\n\\begin{flushright}\n')
1895 self
.body
.append(prefix
)
1896 self
.context
.append(suffix
)
1898 def depart_attribution(self
, node
):
1899 self
.body
.append(self
.context
.pop() + '\n')
1900 self
.body
.append('\\end{flushright}\n')
1902 def visit_status(self
, node
):
1903 self
.visit_docinfo_item(node
, 'status')
1905 def depart_status(self
, node
):
1906 self
.depart_docinfo_item(node
)
1908 def visit_strong(self
, node
):
1909 self
.body
.append('\\textbf{')
1910 self
.literal_block_stack
.append('\\textbf{')
1912 def depart_strong(self
, node
):
1913 self
.body
.append('}')
1914 self
.literal_block_stack
.pop()
1916 def visit_substitution_definition(self
, node
):
1917 raise nodes
.SkipNode
1919 def visit_substitution_reference(self
, node
):
1920 self
.unimplemented_visit(node
)
1922 def visit_subtitle(self
, node
):
1923 if isinstance(node
.parent
, nodes
.sidebar
):
1924 self
.body
.append('~\\\\\n\\textbf{')
1925 self
.context
.append('}\n\\smallskip\n')
1926 elif isinstance(node
.parent
, nodes
.document
):
1927 self
.title
= self
.title
+ \
1928 '\\\\\n\\large{%s}\n' % self
.encode(node
.astext())
1929 raise nodes
.SkipNode
1930 elif isinstance(node
.parent
, nodes
.section
):
1931 self
.body
.append('\\textbf{')
1932 self
.context
.append('}\\vspace{0.2cm}\n\n\\noindent ')
1934 def depart_subtitle(self
, node
):
1935 self
.body
.append(self
.context
.pop())
1937 def visit_system_message(self
, node
):
1940 def depart_system_message(self
, node
):
1941 self
.body
.append('\n')
1943 def visit_table(self
, node
):
1944 if self
.active_table
.is_open():
1945 self
.table_stack
.append(self
.active_table
)
1946 # nesting longtable does not work (e.g. 2007-04-18)
1947 self
.active_table
= Table('tabular',self
.settings
.table_style
)
1948 self
.active_table
.open()
1949 for cl
in node
['classes']:
1950 self
.active_table
.set_table_style(cl
)
1951 self
.body
.append('\n' + self
.active_table
.get_opening())
1953 def depart_table(self
, node
):
1954 self
.body
.append(self
.active_table
.get_closing() + '\n')
1955 self
.active_table
.close()
1956 if len(self
.table_stack
)>0:
1957 self
.active_table
= self
.table_stack
.pop()
1959 self
.active_table
.set_table_style(self
.settings
.table_style
)
1961 def visit_target(self
, node
):
1962 # BUG: why not (refuri or refid or refname) means not footnote ?
1963 if not (node
.has_key('refuri') or node
.has_key('refid')
1964 or node
.has_key('refname')):
1965 for id in node
['ids']:
1966 self
.body
.append('\\hypertarget{%s}{' % id)
1967 self
.context
.append('}' * len(node
['ids']))
1968 elif node
.get("refid"):
1969 self
.body
.append('\\hypertarget{%s}{' % node
.get("refid"))
1970 self
.context
.append('}')
1972 self
.context
.append('')
1974 def depart_target(self
, node
):
1975 self
.body
.append(self
.context
.pop())
1977 def visit_tbody(self
, node
):
1978 # BUG write preamble if not yet done (colspecs not [])
1979 # for tables without heads.
1980 if not self
.active_table
.get('preamble written'):
1981 self
.visit_thead(None)
1982 # self.depart_thead(None)
1984 def depart_tbody(self
, node
):
1987 def visit_term(self
, node
):
1988 self
.body
.append('\\item[{')
1990 def depart_term(self
, node
):
1991 # definition list term.
1992 # \leavevmode results in a line break if the term is followed by a item list.
1993 self
.body
.append('}] \leavevmode ')
1995 def visit_tgroup(self
, node
):
1996 #self.body.append(self.starttag(node, 'colgroup'))
1997 #self.context.append('</colgroup>\n')
2000 def depart_tgroup(self
, node
):
2003 def visit_thead(self
, node
):
2004 self
.body
.append('{%s}\n' % self
.active_table
.get_colspecs())
2005 if self
.active_table
.caption
:
2006 self
.body
.append('\\caption{%s}\\\\\n' % self
.active_table
.caption
)
2007 self
.active_table
.set('preamble written',1)
2008 # TODO longtable supports firsthead and lastfoot too.
2009 self
.body
.extend(self
.active_table
.visit_thead())
2011 def depart_thead(self
, node
):
2012 # the table header written should be on every page
2014 self
.body
.extend(self
.active_table
.depart_thead())
2015 # and the firsthead => \endfirsthead
2016 # BUG i want a "continued from previous page" on every not
2017 # firsthead, but then we need the header twice.
2019 # there is a \endfoot and \endlastfoot too.
2020 # but we need the number of columns to
2021 # self.body.append('\\multicolumn{%d}{c}{"..."}\n' % number_of_columns)
2022 # self.body.append('\\hline\n\\endfoot\n')
2023 # self.body.append('\\hline\n')
2024 # self.body.append('\\endlastfoot\n')
2026 def visit_tip(self
, node
):
2027 self
.visit_admonition(node
, 'tip')
2029 def depart_tip(self
, node
):
2030 self
.depart_admonition()
2032 def bookmark(self
, node
):
2033 """Append latex href and pdfbookmarks for titles.
2035 if node
.parent
['ids']:
2036 for id in node
.parent
['ids']:
2037 self
.body
.append('\\hypertarget{%s}{}\n' % id)
2038 if not self
.use_latex_toc
:
2039 # BUG level depends on style. pdflatex allows level 0 to 3
2040 # ToC would be the only on level 0 so i choose to decrement the rest.
2041 # "Table of contents" bookmark to see the ToC. To avoid this
2042 # we set all zeroes to one.
2043 l
= self
.section_level
2046 # pdftex does not like "_" subscripts in titles
2047 text
= self
.encode(node
.astext())
2048 for id in node
.parent
['ids']:
2049 self
.body
.append('\\pdfbookmark[%d]{%s}{%s}\n' % \
2052 def visit_title(self
, node
):
2053 """Only 3 section levels are supported by LaTeX article (AFAIR)."""
2055 if isinstance(node
.parent
, nodes
.topic
):
2056 # the table of contents.
2058 if ('contents' in self
.topic_classes
2059 and self
.use_latex_toc
):
2060 self
.body
.append('\\renewcommand{\\contentsname}{')
2061 self
.context
.append('}\n\\tableofcontents\n\n\\bigskip\n')
2062 elif ('abstract' in self
.topic_classes
2063 and self
.settings
.use_latex_abstract
):
2064 raise nodes
.SkipNode
2065 else: # or section titles before the table of contents.
2066 # BUG: latex chokes on center environment with
2067 # "perhaps a missing item", therefore we use hfill.
2068 self
.body
.append('\\subsubsection*{~\\hfill ')
2069 # the closing brace for subsection.
2070 self
.context
.append('\\hfill ~}\n')
2071 # TODO: for admonition titles before the first section
2072 # either specify every possible node or ... ?
2073 elif isinstance(node
.parent
, nodes
.sidebar
) \
2074 or isinstance(node
.parent
, nodes
.admonition
):
2075 self
.body
.append('\\textbf{\\large ')
2076 self
.context
.append('}\n\\smallskip\n')
2077 elif isinstance(node
.parent
, nodes
.table
):
2078 # caption must be written after column spec
2079 self
.active_table
.caption
= self
.encode(node
.astext())
2080 raise nodes
.SkipNode
2081 elif self
.section_level
== 0:
2083 self
.title
= self
.encode(node
.astext())
2084 if not self
.pdfinfo
== None:
2085 self
.pdfinfo
.append( 'pdftitle={%s}' % self
.encode(node
.astext()) )
2086 raise nodes
.SkipNode
2088 self
.body
.append('\n\n')
2089 self
.body
.append('%' + '_' * 75)
2090 self
.body
.append('\n\n')
2093 if self
.use_latex_toc
:
2098 section_name
= self
.d_class
.section(self
.section_level
)
2099 self
.body
.append('\\%s%s{' % (section_name
, section_star
))
2100 # MAYBE postfix paragraph and subparagraph with \leavemode to
2101 # ensure floatables stay in the section and text starts on a new line.
2102 self
.context
.append('}\n')
2104 def depart_title(self
, node
):
2105 self
.body
.append(self
.context
.pop())
2106 for id in node
.parent
['ids']:
2107 self
.body
.append('\\label{%s}\n' % id)
2109 def visit_topic(self
, node
):
2110 self
.topic_classes
= node
['classes']
2111 if ('abstract' in self
.topic_classes
2112 and self
.settings
.use_latex_abstract
):
2113 self
.body
.append('\\begin{abstract}\n')
2115 def depart_topic(self
, node
):
2116 if ('abstract' in self
.topic_classes
2117 and self
.settings
.use_latex_abstract
):
2118 self
.body
.append('\\end{abstract}\n')
2119 self
.topic_classes
= []
2120 if 'contents' in node
['classes'] and self
.use_latex_toc
:
2123 self
.body
.append('\n')
2125 def visit_inline(self
, node
): # titlereference
2126 classes
= node
.get('classes', ['Unknown', ])
2128 self
.body
.append( '\\docutilsrole%s{' % cls
)
2129 self
.context
.append('}'*len(classes
))
2131 def depart_inline(self
, node
):
2132 self
.body
.append(self
.context
.pop())
2134 def visit_rubric(self
, node
):
2135 self
.body
.append('\\rubric{')
2136 self
.context
.append('}\n')
2138 def depart_rubric(self
, node
):
2139 self
.body
.append(self
.context
.pop())
2141 def visit_transition(self
, node
):
2142 self
.body
.append('\n\n')
2143 self
.body
.append('%' + '_' * 75)
2144 self
.body
.append('\n\\hspace*{\\fill}\\hrulefill\\hspace*{\\fill}')
2145 self
.body
.append('\n\n')
2147 def depart_transition(self
, node
):
2150 def visit_version(self
, node
):
2151 self
.visit_docinfo_item(node
, 'version')
2153 def depart_version(self
, node
):
2154 self
.depart_docinfo_item(node
)
2156 def visit_warning(self
, node
):
2157 self
.visit_admonition(node
, 'warning')
2159 def depart_warning(self
, node
):
2160 self
.depart_admonition()
2162 def unimplemented_visit(self
, node
):
2163 raise NotImplementedError('visiting unimplemented node type: %s'
2164 % node
.__class
__.__name
__)
2166 # def unknown_visit(self, node):
2167 # def default_visit(self, node):
2169 # vim: set ts=4 et ai :