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()
177 for attr
in self
.visitor_attributes
:
178 setattr(self
, attr
, getattr(visitor
, attr
))
180 def assemble_parts(self
):
181 writers
.Writer
.assemble_parts(self
)
182 for part
in self
.visitor_attributes
:
183 self
.parts
[part
] = ''.join(getattr(self
, part
))
190 * LaTeX does not support multiple tocs in one document.
191 (might be no limitation except for docutils documentation)
193 The "minitoc" latex package can produce per-chapter tocs in
194 book and report document classes.
198 * linewidth - width of a line in the local environment
199 * textwidth - the width of text on the page
201 Maybe always use linewidth ?
203 *Bug* inside a minipage a (e.g. Sidebar) the linewidth is
204 not changed, needs fix in docutils so that tables
207 So we add locallinewidth set it initially and
208 on entering sidebar and reset on exit.
212 """Language specifics for LaTeX."""
213 # country code by a.schlock.
214 # partly manually converted from iso and babel stuff, dialects and some
216 'no': 'norsk', #XXX added by hand ( forget about nynorsk?)
217 'gd': 'scottish', #XXX added by hand
218 'hu': 'magyar', #XXX added by hand
219 'pt': 'portuguese',#XXX added by hand
229 # french, francais, canadien, acadian
230 'de': 'ngerman', #XXX rather than german
231 # ngerman, naustrian, german, germanb, austrian
234 # english, USenglish, american, UKenglish, british, canadian
260 def __init__(self
,lang
):
262 # pdflatex does not produce double quotes for ngerman in tt.
263 self
.double_quote_replacment
= None
264 if re
.search('^de',self
.language
):
265 #self.quotes = ("\"`", "\"'")
266 self
.quotes
= ('{\\glqq}', '{\\grqq}')
267 self
.double_quote_replacment
= "{\\dq}"
268 elif re
.search('^it',self
.language
):
269 self
.quotes
= ("``", "''")
270 self
.double_quote_replacment
= r
'{\char`\"}'
272 self
.quotes
= ("``", "''")
275 def next_quote(self
):
276 q
= self
.quotes
[self
.quote_index
]
277 self
.quote_index
= (self
.quote_index
+1)%2
280 def quote_quotes(self
,text
):
282 for part
in text
.split('"'):
286 t
+= self
.next_quote() + part
289 def double_quotes_in_tt (self
,text
):
290 if not self
.double_quote_replacment
:
292 return text
.replace('"', self
.double_quote_replacment
)
294 def get_language(self
):
295 if self
._ISO
639_TO
_BABEL
.has_key(self
.language
):
296 return self
._ISO
639_TO
_BABEL
[self
.language
]
299 l
= self
.language
.split("_")[0]
300 if self
._ISO
639_TO
_BABEL
.has_key(l
):
301 return self
._ISO
639_TO
_BABEL
[l
]
306 'optionlist_environment' : [
307 '\\newcommand{\\optionlistlabel}[1]{\\bf #1 \\hfill}\n'
308 '\\newenvironment{optionlist}[1]\n'
310 ' {\\setlength{\\labelwidth}{#1}\n'
311 ' \\setlength{\\rightmargin}{1cm}\n'
312 ' \\setlength{\\leftmargin}{\\rightmargin}\n'
313 ' \\addtolength{\\leftmargin}{\\labelwidth}\n'
314 ' \\addtolength{\\leftmargin}{\\labelsep}\n'
315 ' \\renewcommand{\\makelabel}{\\optionlistlabel}}\n'
318 'lineblock_environment' : [
319 '\\newlength{\\lineblockindentation}\n'
320 '\\setlength{\\lineblockindentation}{2.5em}\n'
321 '\\newenvironment{lineblock}[1]\n'
323 ' {\\setlength{\\partopsep}{\\parskip}\n'
324 ' \\addtolength{\\partopsep}{\\baselineskip}\n'
325 ' \\topsep0pt\\itemsep0.15\\baselineskip\\parsep0pt\n'
330 'footnote_floats' : [
331 '% begin: floats for footnotes tweaking.\n',
332 '\\setlength{\\floatsep}{0.5em}\n',
333 '\\setlength{\\textfloatsep}{\\fill}\n',
334 '\\addtolength{\\textfloatsep}{3em}\n',
335 '\\renewcommand{\\textfraction}{0.5}\n',
336 '\\renewcommand{\\topfraction}{0.5}\n',
337 '\\renewcommand{\\bottomfraction}{0.5}\n',
338 '\\setcounter{totalnumber}{50}\n',
339 '\\setcounter{topnumber}{50}\n',
340 '\\setcounter{bottomnumber}{50}\n',
341 '% end floats for footnotes\n',
344 '% some commands, that could be overwritten in the style file.\n'
345 '\\newcommand{\\rubric}[1]'
346 '{\\subsection*{~\\hfill {\\it #1} \\hfill ~}}\n'
347 '\\newcommand{\\titlereference}[1]{\\textsl{#1}}\n'
348 '% end of "some commands"\n',
353 """Details of a LaTeX document class."""
355 def __init__(self
, document_class
, with_part
=False):
356 self
.document_class
= document_class
357 self
._with
_part
= with_part
359 def section(self
, level
):
360 """ Return the section name at the given level for the specific
363 Level is 1,2,3..., as level 0 is the title."""
365 sections
= [ 'section', 'subsection', 'subsubsection',
366 'paragraph', 'subparagraph' ]
367 if self
.document_class
in ('book', 'report', 'scrartcl', 'scrbook'):
368 sections
.insert(0, 'chapter')
370 sections
.insert(0, 'part')
371 if level
<= len(sections
):
372 return sections
[level
-1]
377 """ Manage a table while traversing.
378 Maybe change to a mixin defining the visit/departs, but then
379 class Table internal variables are in the Translator.
383 * standard: horizontal and vertical lines
384 * booktabs (requires booktabs latex package): only horizontal lines
385 * nolines, borderless : no lines
387 def __init__(self
,latex_type
,table_style
):
388 self
._latex
_type
= latex_type
389 self
._table
_style
= table_style
391 # miscellaneous attributes
402 self
._in
_head
= 0 # maybe context with search
405 self
._col
_specs
= None
412 def set_table_style(self
, table_style
):
413 if not table_style
in ('standard','booktabs','borderless','nolines'):
415 self
._table
_style
= table_style
417 def used_packages(self
):
418 if self
._table
_style
== 'booktabs':
419 return '\\usepackage{booktabs}\n'
421 def get_latex_type(self
):
422 return self
._latex
_type
424 def set(self
,attr
,value
):
425 self
._attrs
[attr
] = value
427 if self
._attrs
.has_key(attr
):
428 return self
._attrs
[attr
]
430 def get_vertical_bar(self
):
431 if self
._table
_style
== 'standard':
434 # horizontal lines are drawn below a row, because we.
435 def get_opening(self
):
436 if self
._latex
_type
== 'longtable':
437 # otherwise longtable might move before paragraph and subparagraph
438 prefix
= '\\leavevmode\n'
441 return '%s\\begin{%s}[c]' % (prefix
, self
._latex
_type
)
442 def get_closing(self
):
444 if self
._table
_style
== 'booktabs':
445 line
= '\\bottomrule\n'
446 elif self
._table
_style
== 'standard':
448 return '%s\\end{%s}' % (line
,self
._latex
_type
)
450 def visit_colspec(self
, node
):
451 self
._col
_specs
.append(node
)
452 # "stubs" list is an attribute of the tgroup element:
453 self
.stubs
.append(node
.attributes
.get('stub'))
455 def get_colspecs(self
):
457 Return column specification for longtable.
459 Assumes reST line length being 80 characters.
460 Table width is hairy.
466 usually gets to narrow, therefore we add 1 (fiddlefactor).
471 # first see if we get too wide.
472 for node
in self
._col
_specs
:
473 colwidth
= float(node
['colwidth']+1) / width
474 total_width
+= colwidth
477 # donot make it full linewidth
479 if total_width
> 1.0:
480 factor
/= total_width
481 bar
= self
.get_vertical_bar()
482 latex_table_spec
= ""
483 for node
in self
._col
_specs
:
484 colwidth
= factor
* float(node
['colwidth']+1) / width
485 self
._col
_width
.append(colwidth
+0.005)
486 self
._rowspan
.append(0)
487 latex_table_spec
+= "%sp{%.3f\\locallinewidth}" % (bar
,colwidth
+0.005)
488 return latex_table_spec
+bar
490 def get_column_width(self
):
491 """ return columnwidth for current cell (not multicell)
493 return "%.2f\\locallinewidth" % self
._col
_width
[self
._cell
_in
_row
-1]
495 def visit_thead(self
):
497 if self
._table
_style
== 'standard':
499 elif self
._table
_style
== 'booktabs':
500 return ['\\toprule\n']
502 def depart_thead(self
):
504 #if self._table_style == 'standard':
505 # a.append('\\hline\n')
506 if self
._table
_style
== 'booktabs':
507 a
.append('\\midrule\n')
508 if self
._latex
_type
== 'longtable':
509 a
.append('\\endhead\n')
510 # for longtable one could add firsthead, foot and lastfoot
514 self
._cell
_in
_row
= 0
515 def depart_row(self
):
517 self
._cell
_in
_row
= None # remove cell counter
518 for i
in range(len(self
._rowspan
)):
519 if (self
._rowspan
[i
]>0):
520 self
._rowspan
[i
] -= 1
522 if self
._table
_style
== 'standard':
524 for i
in range(len(self
._rowspan
)):
525 if (self
._rowspan
[i
]<=0):
527 if len(rowspans
)==len(self
._rowspan
):
528 res
.append('\\hline\n')
535 c_start
= rowspans
.pop()
538 cline
+= '\\cline{%d-%d}\n' % (c_start
,c_start
)
542 def set_rowspan(self
,cell
,value
):
544 self
._rowspan
[cell
] = value
547 def get_rowspan(self
,cell
):
549 return self
._rowspan
[cell
]
552 def get_entry_number(self
):
553 return self
._cell
_in
_row
554 def visit_entry(self
):
555 self
._cell
_in
_row
+= 1
556 def is_stub_column(self
):
557 if len(self
.stubs
) >= self
._cell
_in
_row
:
558 return self
.stubs
[self
._cell
_in
_row
-1]
562 class LaTeXTranslator(nodes
.NodeVisitor
):
564 # When options are given to the documentclass, latex will pass them
565 # to other packages, as done with babel.
566 # Dummy settings might be taken from document settings
571 latex_head
= '\\documentclass[%s]{%s}\n'
572 linking
= '\\usepackage[colorlinks=%s,linkcolor=%s,urlcolor=%s]{hyperref}\n'
573 stylesheet
= '\\input{%s}\n'
574 # add a generated on day , machine by user using docutils version.
575 generator
= '% generated by Docutils <http://docutils.sourceforge.net/>\n'
576 # Config setting defaults
577 # -----------------------
579 # use latex tableofcontents or let docutils do it.
582 # TODO: use mixins for different implementations.
583 # list environment for docinfo. else tabularx
584 use_optionlist_for_docinfo
= 0 # NOT YET IN USE
586 # Use compound enumerations (1.A.1.)
587 compound_enumerators
= 0
589 # If using compound enumerations, include section information.
590 section_prefix_for_enumerators
= 0
592 # This is the character that separates the section ("." subsection ...)
593 # prefix from the regular list enumerator.
594 section_enumerator_separator
= '-'
597 hyperlink_color
= "blue"
599 def __init__(self
, document
):
600 nodes
.NodeVisitor
.__init
__(self
, document
)
601 self
.settings
= settings
= document
.settings
602 self
.latex_encoding
= self
.to_latex_encoding(settings
.output_encoding
)
603 self
.use_latex_toc
= settings
.use_latex_toc
604 self
.use_latex_docinfo
= settings
.use_latex_docinfo
605 self
.use_latex_footnotes
= settings
.use_latex_footnotes
606 self
._use
_latex
_citations
= settings
.use_latex_citations
607 self
._reference
_label
= settings
.reference_label
608 self
.hyperlink_color
= settings
.hyperlink_color
609 self
.compound_enumerators
= settings
.compound_enumerators
610 self
.font_encoding
= settings
.font_encoding
611 self
.section_prefix_for_enumerators
= (
612 settings
.section_prefix_for_enumerators
)
613 self
.section_enumerator_separator
= (
614 settings
.section_enumerator_separator
.replace('_', '\\_'))
615 if self
.hyperlink_color
== '0':
616 self
.hyperlink_color
= 'black'
617 self
.colorlinks
= 'false'
619 self
.colorlinks
= 'true'
621 if self
.settings
.use_bibtex
:
622 self
.bibtex
= self
.settings
.use_bibtex
.split(",",1)
623 # TODO avoid errors on not declared citations.
626 # language: labels, bibliographic_fields, and author_separators.
627 # to allow writing labes for specific languages.
628 self
.language
= languages
.get_language(settings
.language_code
)
629 self
.babel
= Babel(settings
.language_code
)
630 self
.author_separator
= self
.language
.author_separators
[0]
631 self
.d_options
= self
.settings
.documentoptions
632 if self
.babel
.get_language():
633 self
.d_options
+= ',%s' % self
.babel
.get_language()
635 self
.d_class
= DocumentClass(settings
.documentclass
,
636 settings
.use_part_section
)
637 # object for a table while proccessing.
638 self
.table_stack
= []
639 self
.active_table
= Table('longtable',settings
.table_style
)
641 # HACK. Should have more sophisticated typearea handling.
642 if settings
.documentclass
.find('scr') == -1:
643 self
.typearea
= '\\usepackage[DIV12]{typearea}\n'
645 if self
.d_options
.find('DIV') == -1 and self
.d_options
.find('BCOR') == -1:
646 self
.typearea
= '\\typearea{12}\n'
650 if self
.font_encoding
== 'OT1':
652 elif self
.font_encoding
== '':
653 fontenc_header
= '\\usepackage{ae}\n\\usepackage{aeguill}\n'
655 fontenc_header
= '\\usepackage[%s]{fontenc}\n' % (self
.font_encoding
,)
656 if self
.latex_encoding
.startswith('utf8'):
657 input_encoding
= '\\usepackage{ucs}\n\\usepackage[utf8x]{inputenc}\n'
659 input_encoding
= '\\usepackage[%s]{inputenc}\n' % self
.latex_encoding
660 if self
.settings
.graphicx_option
== '':
661 self
.graphicx_package
= '\\usepackage{graphicx}\n'
662 elif self
.settings
.graphicx_option
.lower() == 'auto':
663 self
.graphicx_package
= '\n'.join(
664 ('%Check if we are compiling under latex or pdflatex',
665 '\\ifx\\pdftexversion\\undefined',
666 ' \\usepackage{graphicx}',
668 ' \\usepackage[pdftex]{graphicx}',
671 self
.graphicx_package
= (
672 '\\usepackage[%s]{graphicx}\n' % self
.settings
.graphicx_option
)
675 self
.latex_head
% (self
.d_options
,self
.settings
.documentclass
),
676 '\\usepackage{babel}\n', # language is in documents settings.
678 '\\usepackage{shortvrb}\n', # allows verb in footnotes.
680 # * tabularx: for docinfo, automatic width of columns, always on one page.
681 '\\usepackage{tabularx}\n',
682 '\\usepackage{longtable}\n',
683 self
.active_table
.used_packages(),
684 # possible other packages.
686 # * ltxtable is a combination of tabularx and longtable (pagebreaks).
689 # extra space between text in tables and the line above them
690 '\\setlength{\\extrarowheight}{2pt}\n',
691 '\\usepackage{amsmath}\n', # what fore amsmath.
692 self
.graphicx_package
,
693 '\\usepackage{color}\n',
694 '\\usepackage{multirow}\n',
695 '\\usepackage{ifthen}\n', # before hyperref!
696 self
.linking
% (self
.colorlinks
, self
.hyperlink_color
, self
.hyperlink_color
),
700 '\\newlength{\\admonitionwidth}\n',
701 '\\setlength{\\admonitionwidth}{0.9\\textwidth}\n'
702 # width for docinfo tablewidth
703 '\\newlength{\\docinfowidth}\n',
704 '\\setlength{\\docinfowidth}{0.9\\textwidth}\n'
705 # linewidth of current environment, so tables are not wider
706 # than the sidebar: using locallinewidth seems to defer evaluation
707 # of linewidth, this is fixing it.
708 '\\newlength{\\locallinewidth}\n',
711 self
.head_prefix
.extend( latex_headings
['optionlist_environment'] )
712 self
.head_prefix
.extend( latex_headings
['lineblock_environment'] )
713 self
.head_prefix
.extend( latex_headings
['footnote_floats'] )
714 self
.head_prefix
.extend( latex_headings
['some_commands'] )
715 ## stylesheet is last: so it might be possible to overwrite defaults.
716 stylesheet
= utils
.get_stylesheet_reference(settings
)
718 settings
.record_dependencies
.add(stylesheet
)
719 self
.head_prefix
.append(self
.stylesheet
% (stylesheet
))
721 if self
.linking
: # and maybe check for pdf
723 self
.pdfauthor
= None
724 # pdftitle, pdfsubject, pdfauthor, pdfkeywords,
725 # pdfcreator, pdfproducer
728 # NOTE: Latex wants a date and an author, rst puts this into
729 # docinfo, so normally we do not want latex author/date handling.
730 # latex article has its own handling of date and author, deactivate.
731 # self.astext() adds \title{...} \author{...} \date{...}, even if the
732 # "..." are empty strings.
734 # separate title, so we can appen subtitle.
736 # if use_latex_docinfo: collects lists of author/organization/contact/address lines
737 self
.author_stack
= []
740 self
.body_prefix
= ['\\raggedbottom\n']
742 self
.body_suffix
= ['\n']
743 self
.section_level
= 0
745 self
.topic_classes
= []
746 # column specification for tables
747 self
.table_caption
= None
751 # verbatim: to tell encode not to encode.
753 # insert_newline: to tell encode to replace blanks by "~".
754 self
.insert_none_breaking_blanks
= 0
755 # insert_newline: to tell encode to add latex newline.
756 self
.insert_newline
= 0
757 # mbox_newline: to tell encode to add mbox and newline.
758 self
.mbox_newline
= 0
759 # inside citation reference labels underscores dont need to be escaped.
760 self
.inside_citation_reference_label
= 0
762 # Stack of section counters so that we don't have to use_latex_toc.
763 # This will grow and shrink as processing occurs.
764 # Initialized for potential first-level sections.
765 self
._section
_number
= [0]
767 # The current stack of enumerations so that we can expand
768 # them into a compound enumeration.
769 self
._enumeration
_counters
= []
771 # The maximum number of enumeration counters we've used.
772 # If we go beyond this number, we need to create a new
773 # counter; otherwise, just reuse an old one.
774 self
._max
_enumeration
_counters
= 0
780 # inside literal block: no quote mangling.
781 self
.literal_block
= 0
782 self
.literal_block_stack
= []
784 # true when encoding in math mode
787 def to_latex_encoding(self
,docutils_encoding
):
789 Translate docutils encoding name into latex's.
791 Default fallback method is remove "-" and "_" chars from docutils_encoding.
794 tr
= { "iso-8859-1": "latin1", # west european
795 "iso-8859-2": "latin2", # east european
796 "iso-8859-3": "latin3", # esperanto, maltese
797 "iso-8859-4": "latin4", # north european,scandinavian, baltic
798 "iso-8859-5": "iso88595", # cyrillic (ISO)
799 "iso-8859-9": "latin5", # turkish
800 "iso-8859-15": "latin9", # latin9, update to latin1.
801 "mac_cyrillic": "maccyr", # cyrillic (on Mac)
802 "windows-1251": "cp1251", # cyrillic (on Windows)
803 "koi8-r": "koi8-r", # cyrillic (Russian)
804 "koi8-u": "koi8-u", # cyrillic (Ukrainian)
805 "windows-1250": "cp1250", #
806 "windows-1252": "cp1252", #
807 "us-ascii": "ascii", # ASCII (US)
808 # unmatched encodings
810 #"": "ansinew", # windows 3.1 ansi
811 #"": "ascii", # ASCII encoding for the range 32--127.
812 #"": "cp437", # dos latine us
813 #"": "cp850", # dos latin 1
814 #"": "cp852", # dos latin 2
817 #"iso-8859-6": "" # arabic
818 #"iso-8859-7": "" # greek
819 #"iso-8859-8": "" # hebrew
820 #"iso-8859-10": "" # latin6, more complete iso-8859-4
822 if tr
.has_key(docutils_encoding
.lower()):
823 return tr
[docutils_encoding
.lower()]
824 # convert: latin-1 and utf-8 and similar things
825 return docutils_encoding
.replace("_", "").replace("-", "").lower()
827 def language_label(self
, docutil_label
):
828 return self
.language
.labels
[docutil_label
]
830 latex_equivalents
= {
840 u
'\u2020' : '{\\dag}',
841 u
'\u2021' : '{\\ddag}',
842 u
'\u2026' : '{\\dots}',
843 u
'\u2122' : '{\\texttrademark}',
844 u
'\u21d4' : '{$\\Leftrightarrow$}',
848 def unicode_to_latex(self
,text
):
850 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252124
851 # Only some special chracters are translated, for documents with many
852 # utf-8 chars one should use the LaTeX unicode package.
853 for uchar
in self
.latex_equivalents
.keys():
854 text
= text
.replace(uchar
,self
.latex_equivalents
[uchar
])
857 def ensure_math(self
, text
):
858 if not self
.__dict
__.has_key('ensure_math_re'):
860 # lnot,pm,twosuperior,threesuperior,mu,onesuperior,times,div
861 'latin1' : '\xac\xb1\xb2\xb3\xb5\xb9\xd7\xf7' ,
862 # also latin5 and latin9
864 self
.ensure_math_re
= re
.compile('([%s])' % chars
['latin1'])
865 text
= self
.ensure_math_re
.sub(r
'\\ensuremath{\1}', text
)
868 def encode(self
, text
):
870 Encode special characters (``# $ % & ~ _ ^ \ { }``) in `text` & return
872 # Escaping with a backslash does not help with backslashes, ~ and ^.
874 # < > are only available in math-mode or tt font. (really ?)
875 # $ starts math- mode.
879 # compile the regexps once. do it here so one can see them.
882 if not self
.__dict
__.has_key('encode_re_braces'):
883 self
.encode_re_braces
= re
.compile(r
'([{}])')
884 text
= self
.encode_re_braces
.sub(r
'{\\\1}',text
)
885 if not self
.__dict
__.has_key('encode_re_bslash'):
886 # find backslash: except in the form '{\{}' or '{\}}'.
887 self
.encode_re_bslash
= re
.compile(r
'(?<!{)(\\)(?![{}]})')
888 # then the backslash: except in the form from line above:
889 # either '{\{}' or '{\}}'.
890 text
= self
.encode_re_bslash
.sub(r
'{\\textbackslash}', text
)
893 text
= text
.replace("$", '{\\$}')
894 if not ( self
.literal_block
or self
.literal
or self
.mathmode
):
895 # the vertical bar: in mathmode |,\vert or \mid
896 # in textmode \textbar
897 text
= text
.replace("|", '{\\textbar}')
898 text
= text
.replace("<", '{\\textless}')
899 text
= text
.replace(">", '{\\textgreater}')
901 text
= text
.replace("&", '{\\&}')
903 # * verb|^| does not work in mbox.
904 # * mathmode has wedge. hat{~} would also work.
905 # text = text.replace("^", '{\\ensuremath{^\\wedge}}')
906 text
= text
.replace("^", '{\\textasciicircum}')
907 text
= text
.replace("%", '{\\%}')
908 text
= text
.replace("#", '{\\#}')
909 text
= text
.replace("~", '{\\textasciitilde}')
910 # Separate compound characters, e.g. "--" to "-{}-". (The
911 # actual separation is done later; see below.)
913 if self
.literal_block
or self
.literal
:
914 # In monospace-font, we also separate ",,", "``" and "''"
915 # and some other characters which can't occur in
917 separate_chars
+= ',`\'"<>'
918 # pdflatex does not produce doublequotes for ngerman.
919 text
= self
.babel
.double_quotes_in_tt(text
)
920 if self
.font_encoding
== 'OT1':
921 # We're using OT1 font-encoding and have to replace
922 # underscore by underlined blank, because this has
924 text
= text
.replace('_', '{\\underline{ }}')
925 # And the tt-backslash doesn't work in OT1, so we use
927 text
= text
.replace('\\textbackslash', '\\reflectbox{/}')
929 text
= text
.replace('_', '{\\_}')
931 text
= self
.babel
.quote_quotes(text
)
932 if not self
.inside_citation_reference_label
:
933 text
= text
.replace("_", '{\\_}')
934 for char
in separate_chars
* 2:
935 # Do it twice ("* 2") becaues otherwise we would replace
937 text
= text
.replace(char
+ char
, char
+ '{}' + char
)
938 if self
.insert_newline
or self
.literal_block
:
939 # Insert a blank before the newline, to avoid
940 # ! LaTeX Error: There's no line here to end.
941 text
= text
.replace("\n", '~\\\\\n')
942 elif self
.mbox_newline
:
943 if self
.literal_block
:
944 closings
= "}" * len(self
.literal_block_stack
)
945 openings
= "".join(self
.literal_block_stack
)
949 text
= text
.replace("\n", "%s}\\\\\n\\mbox{%s" % (closings
,openings
))
950 text
= text
.replace('[', '{[}').replace(']', '{]}')
951 if self
.insert_none_breaking_blanks
:
952 text
= text
.replace(' ', '~')
953 if self
.latex_encoding
!= 'utf8':
954 text
= self
.unicode_to_latex(text
)
955 text
= self
.ensure_math(text
)
958 def attval(self
, text
,
959 whitespace
=re
.compile('[\n\r\t\v\f]')):
960 """Cleanse, encode, and return attribute value text."""
961 return self
.encode(whitespace
.sub(' ', text
))
964 if self
.pdfinfo
is not None and self
.pdfauthor
:
965 self
.pdfinfo
.append('pdfauthor={%s}' % self
.pdfauthor
)
967 pdfinfo
= '\\hypersetup{\n' + ',\n'.join(self
.pdfinfo
) + '\n}\n'
970 head
= '\\title{%s}\n\\author{%s}\n\\date{%s}\n' % \
972 ' \\and\n'.join(['~\\\\\n'.join(author_lines
)
973 for author_lines
in self
.author_stack
]),
975 return ''.join(self
.head_prefix
+ [head
] + self
.head
+ [pdfinfo
]
976 + self
.body_prefix
+ self
.body
+ self
.body_suffix
)
978 def visit_Text(self
, node
):
979 self
.body
.append(self
.encode(node
.astext()))
981 def depart_Text(self
, node
):
984 def visit_address(self
, node
):
985 self
.visit_docinfo_item(node
, 'address')
987 def depart_address(self
, node
):
988 self
.depart_docinfo_item(node
)
990 def visit_admonition(self
, node
, name
=''):
991 self
.body
.append('\\begin{center}\\begin{sffamily}\n')
992 self
.body
.append('\\fbox{\\parbox{\\admonitionwidth}{\n')
994 self
.body
.append('\\textbf{\\large '+ self
.language
.labels
[name
] + '}\n');
995 self
.body
.append('\\vspace{2mm}\n')
998 def depart_admonition(self
, node
=None):
999 self
.body
.append('}}\n') # end parbox fbox
1000 self
.body
.append('\\end{sffamily}\n\\end{center}\n');
1002 def visit_attention(self
, node
):
1003 self
.visit_admonition(node
, 'attention')
1005 def depart_attention(self
, node
):
1006 self
.depart_admonition()
1008 def visit_author(self
, node
):
1009 self
.visit_docinfo_item(node
, 'author')
1011 def depart_author(self
, node
):
1012 self
.depart_docinfo_item(node
)
1014 def visit_authors(self
, node
):
1015 # not used: visit_author is called anyway for each author.
1018 def depart_authors(self
, node
):
1021 def visit_block_quote(self
, node
):
1022 self
.body
.append( '\\begin{quote}\n')
1024 def depart_block_quote(self
, node
):
1025 self
.body
.append( '\\end{quote}\n')
1027 def visit_bullet_list(self
, node
):
1028 if 'contents' in self
.topic_classes
:
1029 if self
.use_latex_toc
:
1030 raise nodes
.SkipNode
1031 self
.body
.append( '\\begin{list}{}{}\n' )
1033 self
.body
.append( '\\begin{itemize}\n' )
1035 def depart_bullet_list(self
, node
):
1036 if 'contents' in self
.topic_classes
:
1037 self
.body
.append( '\\end{list}\n' )
1039 self
.body
.append( '\\end{itemize}\n' )
1041 # Imperfect superscript/subscript handling: mathmode italicizes
1042 # all letters by default.
1043 def visit_superscript(self
, node
):
1044 self
.body
.append('$^{')
1047 def depart_superscript(self
, node
):
1048 self
.body
.append('}$')
1051 def visit_subscript(self
, node
):
1052 self
.body
.append('$_{')
1055 def depart_subscript(self
, node
):
1056 self
.body
.append('}$')
1059 def visit_caption(self
, node
):
1060 self
.body
.append( '\\caption{' )
1062 def depart_caption(self
, node
):
1063 self
.body
.append('}')
1065 def visit_caution(self
, node
):
1066 self
.visit_admonition(node
, 'caution')
1068 def depart_caution(self
, node
):
1069 self
.depart_admonition()
1071 def visit_title_reference(self
, node
):
1072 self
.body
.append( '\\titlereference{' )
1074 def depart_title_reference(self
, node
):
1075 self
.body
.append( '}' )
1077 def visit_citation(self
, node
):
1078 # TODO maybe use cite bibitems
1079 if self
._use
_latex
_citations
:
1080 self
.context
.append(len(self
.body
))
1082 self
.body
.append('\\begin{figure}[b]')
1083 for id in node
['ids']:
1084 self
.body
.append('\\hypertarget{%s}' % id)
1086 def depart_citation(self
, node
):
1087 if self
._use
_latex
_citations
:
1088 size
= self
.context
.pop()
1089 label
= self
.body
[size
]
1090 text
= ''.join(self
.body
[size
+1:])
1091 del self
.body
[size
:]
1092 self
._bibitems
.append([label
, text
])
1094 self
.body
.append('\\end{figure}\n')
1096 def visit_citation_reference(self
, node
):
1097 if self
._use
_latex
_citations
:
1098 self
.body
.append('\\cite{')
1099 self
.inside_citation_reference_label
= 1
1102 if node
.has_key('refid'):
1103 href
= node
['refid']
1104 elif node
.has_key('refname'):
1105 href
= self
.document
.nameids
[node
['refname']]
1106 self
.body
.append('[\\hyperlink{%s}{' % href
)
1108 def depart_citation_reference(self
, node
):
1109 if self
._use
_latex
_citations
:
1110 self
.body
.append('}')
1111 self
.inside_citation_reference_label
= 0
1113 self
.body
.append('}]')
1115 def visit_classifier(self
, node
):
1116 self
.body
.append( '(\\textbf{' )
1118 def depart_classifier(self
, node
):
1119 self
.body
.append( '})\n' )
1121 def visit_colspec(self
, node
):
1122 self
.active_table
.visit_colspec(node
)
1124 def depart_colspec(self
, node
):
1127 def visit_comment(self
, node
):
1128 # Escape end of line by a new comment start in comment text.
1129 self
.body
.append('%% %s \n' % node
.astext().replace('\n', '\n% '))
1130 raise nodes
.SkipNode
1132 def visit_compound(self
, node
):
1135 def depart_compound(self
, node
):
1138 def visit_contact(self
, node
):
1139 self
.visit_docinfo_item(node
, 'contact')
1141 def depart_contact(self
, node
):
1142 self
.depart_docinfo_item(node
)
1144 def visit_container(self
, node
):
1147 def depart_container(self
, node
):
1150 def visit_copyright(self
, node
):
1151 self
.visit_docinfo_item(node
, 'copyright')
1153 def depart_copyright(self
, node
):
1154 self
.depart_docinfo_item(node
)
1156 def visit_danger(self
, node
):
1157 self
.visit_admonition(node
, 'danger')
1159 def depart_danger(self
, node
):
1160 self
.depart_admonition()
1162 def visit_date(self
, node
):
1163 self
.visit_docinfo_item(node
, 'date')
1165 def depart_date(self
, node
):
1166 self
.depart_docinfo_item(node
)
1168 def visit_decoration(self
, node
):
1171 def depart_decoration(self
, node
):
1174 def visit_definition(self
, node
):
1177 def depart_definition(self
, node
):
1178 self
.body
.append('\n')
1180 def visit_definition_list(self
, node
):
1181 self
.body
.append( '\\begin{description}\n' )
1183 def depart_definition_list(self
, node
):
1184 self
.body
.append( '\\end{description}\n' )
1186 def visit_definition_list_item(self
, node
):
1189 def depart_definition_list_item(self
, node
):
1192 def visit_description(self
, node
):
1193 self
.body
.append( ' ' )
1195 def depart_description(self
, node
):
1198 def visit_docinfo(self
, node
):
1200 self
.docinfo
.append('%' + '_'*75 + '\n')
1201 self
.docinfo
.append('\\begin{center}\n')
1202 self
.docinfo
.append('\\begin{tabularx}{\\docinfowidth}{lX}\n')
1204 def depart_docinfo(self
, node
):
1205 self
.docinfo
.append('\\end{tabularx}\n')
1206 self
.docinfo
.append('\\end{center}\n')
1207 self
.body
= self
.docinfo
+ self
.body
1208 # clear docinfo, so field names are no longer appended.
1211 def visit_docinfo_item(self
, node
, name
):
1212 if name
== 'author':
1213 if not self
.pdfinfo
== None:
1214 if not self
.pdfauthor
:
1215 self
.pdfauthor
= self
.attval(node
.astext())
1217 self
.pdfauthor
+= self
.author_separator
+ self
.attval(node
.astext())
1218 if self
.use_latex_docinfo
:
1219 if name
in ('author', 'organization', 'contact', 'address'):
1220 # We attach these to the last author. If any of them precedes
1221 # the first author, put them in a separate "author" group (for
1222 # no better semantics).
1223 if name
== 'author' or not self
.author_stack
:
1224 self
.author_stack
.append([])
1225 if name
== 'address': # newlines are meaningful
1226 self
.insert_newline
= 1
1227 text
= self
.encode(node
.astext())
1228 self
.insert_newline
= 0
1230 text
= self
.attval(node
.astext())
1231 self
.author_stack
[-1].append(text
)
1232 raise nodes
.SkipNode
1233 elif name
== 'date':
1234 self
.date
= self
.attval(node
.astext())
1235 raise nodes
.SkipNode
1236 self
.docinfo
.append('\\textbf{%s}: &\n\t' % self
.language_label(name
))
1237 if name
== 'address':
1238 self
.insert_newline
= 1
1239 self
.docinfo
.append('{\\raggedright\n')
1240 self
.context
.append(' } \\\\\n')
1242 self
.context
.append(' \\\\\n')
1243 self
.context
.append(self
.docinfo
)
1244 self
.context
.append(len(self
.body
))
1246 def depart_docinfo_item(self
, node
):
1247 size
= self
.context
.pop()
1248 dest
= self
.context
.pop()
1249 tail
= self
.context
.pop()
1250 tail
= self
.body
[size
:] + [tail
]
1251 del self
.body
[size
:]
1253 # for address we did set insert_newline
1254 self
.insert_newline
= 0
1256 def visit_doctest_block(self
, node
):
1257 self
.body
.append( '\\begin{verbatim}' )
1260 def depart_doctest_block(self
, node
):
1261 self
.body
.append( '\\end{verbatim}\n' )
1264 def visit_document(self
, node
):
1265 self
.body_prefix
.append('\\begin{document}\n')
1267 if self
.use_latex_docinfo
or len(node
) and isinstance(node
[0], nodes
.title
):
1268 self
.body_prefix
.append('\\maketitle\n')
1269 # alternative use titlepage environment.
1272 self
.body
.append('\n\\setlength{\\locallinewidth}{\\linewidth}\n')
1274 def depart_document(self
, node
):
1275 # TODO insertion point of bibliography should none automatic.
1276 if self
._use
_latex
_citations
and len(self
._bibitems
)>0:
1279 for bi
in self
._bibitems
:
1280 if len(widest_label
)<len(bi
[0]):
1281 widest_label
= bi
[0]
1282 self
.body
.append('\n\\begin{thebibliography}{%s}\n'%widest_label
)
1283 for bi
in self
._bibitems
:
1284 # cite_key: underscores must not be escaped
1285 cite_key
= bi
[0].replace(r
"{\_}","_")
1286 self
.body
.append('\\bibitem[%s]{%s}{%s}\n' % (bi
[0], cite_key
, bi
[1]))
1287 self
.body
.append('\\end{thebibliography}\n')
1289 self
.body
.append('\n\\bibliographystyle{%s}\n' % self
.bibtex
[0])
1290 self
.body
.append('\\bibliography{%s}\n' % self
.bibtex
[1])
1292 self
.body_suffix
.append('\\end{document}\n')
1294 def visit_emphasis(self
, node
):
1295 self
.body
.append('\\emph{')
1296 self
.literal_block_stack
.append('\\emph{')
1298 def depart_emphasis(self
, node
):
1299 self
.body
.append('}')
1300 self
.literal_block_stack
.pop()
1302 def visit_entry(self
, node
):
1303 self
.active_table
.visit_entry()
1305 if self
.active_table
.get_entry_number() == 1:
1306 # if the firstrow is a multirow, this actually is the second row.
1307 # this gets hairy if rowspans follow each other.
1308 if self
.active_table
.get_rowspan(0):
1310 while self
.active_table
.get_rowspan(count
):
1312 self
.body
.append(' & ')
1313 self
.active_table
.visit_entry() # increment cell count
1315 self
.body
.append(' & ')
1318 # IN WORK BUG TODO HACK continues here
1319 # multirow in LaTeX simply will enlarge the cell over several rows
1320 # (the following n if n is positive, the former if negative).
1321 if node
.has_key('morerows') and node
.has_key('morecols'):
1322 raise NotImplementedError('Cells that '
1323 'span multiple rows *and* columns are not supported, sorry.')
1324 if node
.has_key('morerows'):
1325 count
= node
['morerows'] + 1
1326 self
.active_table
.set_rowspan(self
.active_table
.get_entry_number()-1,count
)
1327 self
.body
.append('\\multirow{%d}{%s}{' % \
1328 (count
,self
.active_table
.get_column_width()))
1329 self
.context
.append('}')
1330 # BUG following rows must have empty cells.
1331 elif node
.has_key('morecols'):
1332 # the vertical bar before column is missing if it is the first column.
1333 # the one after always.
1334 if self
.active_table
.get_entry_number() == 1:
1335 bar1
= self
.active_table
.get_vertical_bar()
1338 count
= node
['morecols'] + 1
1339 self
.body
.append('\\multicolumn{%d}{%sl%s}{' % \
1340 (count
, bar1
, self
.active_table
.get_vertical_bar()))
1341 self
.context
.append('}')
1343 self
.context
.append('')
1345 # header / not header
1346 if isinstance(node
.parent
.parent
, nodes
.thead
):
1347 self
.body
.append('\\textbf{')
1348 self
.context
.append('}')
1349 elif self
.active_table
.is_stub_column():
1350 self
.body
.append('\\textbf{')
1351 self
.context
.append('}')
1353 self
.context
.append('')
1355 def depart_entry(self
, node
):
1356 self
.body
.append(self
.context
.pop()) # header / not header
1357 self
.body
.append(self
.context
.pop()) # multirow/column
1358 # if following row is spanned from above.
1359 if self
.active_table
.get_rowspan(self
.active_table
.get_entry_number()):
1360 self
.body
.append(' & ')
1361 self
.active_table
.visit_entry() # increment cell count
1363 def visit_row(self
, node
):
1364 self
.active_table
.visit_row()
1366 def depart_row(self
, node
):
1367 self
.body
.extend(self
.active_table
.depart_row())
1369 def visit_enumerated_list(self
, node
):
1370 # We create our own enumeration list environment.
1371 # This allows to set the style and starting value
1372 # and unlimited nesting.
1373 enum_style
= {'arabic':'arabic',
1374 'loweralpha':'alph',
1375 'upperalpha':'Alph',
1376 'lowerroman':'roman',
1377 'upperroman':'Roman' }
1379 if node
.has_key('suffix'):
1380 enum_suffix
= node
['suffix']
1382 if node
.has_key('prefix'):
1383 enum_prefix
= node
['prefix']
1384 if self
.compound_enumerators
:
1386 if self
.section_prefix_for_enumerators
and self
.section_level
:
1387 for i
in range(self
.section_level
):
1388 pref
+= '%d.' % self
._section
_number
[i
]
1389 pref
= pref
[:-1] + self
.section_enumerator_separator
1391 for ctype
, cname
in self
._enumeration
_counters
:
1392 enum_prefix
+= '\\%s{%s}.' % (ctype
, cname
)
1393 enum_type
= "arabic"
1394 if node
.has_key('enumtype'):
1395 enum_type
= node
['enumtype']
1396 if enum_style
.has_key(enum_type
):
1397 enum_type
= enum_style
[enum_type
]
1399 counter_name
= "listcnt%d" % len(self
._enumeration
_counters
)
1400 self
._enumeration
_counters
.append((enum_type
, counter_name
))
1401 # If we haven't used this counter name before, then create a
1402 # new counter; otherwise, reset & reuse the old counter.
1403 if len(self
._enumeration
_counters
) > self
._max
_enumeration
_counters
:
1404 self
._max
_enumeration
_counters
= len(self
._enumeration
_counters
)
1405 self
.body
.append('\\newcounter{%s}\n' % counter_name
)
1407 self
.body
.append('\\setcounter{%s}{0}\n' % counter_name
)
1409 self
.body
.append('\\begin{list}{%s\\%s{%s}%s}\n' % \
1410 (enum_prefix
,enum_type
,counter_name
,enum_suffix
))
1411 self
.body
.append('{\n')
1412 self
.body
.append('\\usecounter{%s}\n' % counter_name
)
1413 # set start after usecounter, because it initializes to zero.
1414 if node
.has_key('start'):
1415 self
.body
.append('\\addtocounter{%s}{%d}\n' \
1416 % (counter_name
,node
['start']-1))
1417 ## set rightmargin equal to leftmargin
1418 self
.body
.append('\\setlength{\\rightmargin}{\\leftmargin}\n')
1419 self
.body
.append('}\n')
1421 def depart_enumerated_list(self
, node
):
1422 self
.body
.append('\\end{list}\n')
1423 self
._enumeration
_counters
.pop()
1425 def visit_error(self
, node
):
1426 self
.visit_admonition(node
, 'error')
1428 def depart_error(self
, node
):
1429 self
.depart_admonition()
1431 def visit_field(self
, node
):
1432 # real output is done in siblings: _argument, _body, _name
1435 def depart_field(self
, node
):
1436 self
.body
.append('\n')
1437 ##self.body.append('%[depart_field]\n')
1439 def visit_field_argument(self
, node
):
1440 self
.body
.append('%[visit_field_argument]\n')
1442 def depart_field_argument(self
, node
):
1443 self
.body
.append('%[depart_field_argument]\n')
1445 def visit_field_body(self
, node
):
1446 # BUG by attach as text we loose references.
1448 self
.docinfo
.append('%s \\\\\n' % self
.encode(node
.astext()))
1449 raise nodes
.SkipNode
1450 # BUG: what happens if not docinfo
1452 def depart_field_body(self
, node
):
1453 self
.body
.append( '\n' )
1455 def visit_field_list(self
, node
):
1456 if not self
.docinfo
:
1457 self
.body
.append('\\begin{quote}\n')
1458 self
.body
.append('\\begin{description}\n')
1460 def depart_field_list(self
, node
):
1461 if not self
.docinfo
:
1462 self
.body
.append('\\end{description}\n')
1463 self
.body
.append('\\end{quote}\n')
1465 def visit_field_name(self
, node
):
1466 # BUG this duplicates docinfo_item
1468 self
.docinfo
.append('\\textbf{%s}: &\n\t' % self
.encode(node
.astext()))
1469 raise nodes
.SkipNode
1471 self
.body
.append('\\item [')
1473 def depart_field_name(self
, node
):
1474 if not self
.docinfo
:
1475 self
.body
.append(':]')
1477 def visit_figure(self
, node
):
1478 if (not node
.attributes
.has_key('align') or
1479 node
.attributes
['align'] == 'center'):
1480 # centering does not add vertical space like center.
1481 align
= '\n\\centering'
1484 # TODO non vertical space for other alignments.
1485 align
= '\\begin{flush%s}' % node
.attributes
['align']
1486 align_end
= '\\end{flush%s}' % node
.attributes
['align']
1487 self
.body
.append( '\\begin{figure}[htbp]%s\n' % align
)
1488 self
.context
.append( '%s\\end{figure}\n' % align_end
)
1490 def depart_figure(self
, node
):
1491 self
.body
.append( self
.context
.pop() )
1493 def visit_footer(self
, node
):
1494 self
.context
.append(len(self
.body
))
1496 def depart_footer(self
, node
):
1497 start
= self
.context
.pop()
1498 footer
= (['\n\\begin{center}\small\n']
1499 + self
.body
[start
:] + ['\n\\end{center}\n'])
1500 self
.body_suffix
[:0] = footer
1501 del self
.body
[start
:]
1503 def visit_footnote(self
, node
):
1504 if self
.use_latex_footnotes
:
1505 num
,text
= node
.astext().split(None,1)
1506 num
= self
.encode(num
.strip())
1507 self
.body
.append('\\footnotetext['+num
+']')
1508 self
.body
.append('{')
1510 self
.body
.append('\\begin{figure}[b]')
1511 for id in node
['ids']:
1512 self
.body
.append('\\hypertarget{%s}' % id)
1514 def depart_footnote(self
, node
):
1515 if self
.use_latex_footnotes
:
1516 self
.body
.append('}\n')
1518 self
.body
.append('\\end{figure}\n')
1520 def visit_footnote_reference(self
, node
):
1521 if self
.use_latex_footnotes
:
1522 self
.body
.append("\\footnotemark["+self
.encode(node
.astext())+"]")
1523 raise nodes
.SkipNode
1525 if node
.has_key('refid'):
1526 href
= node
['refid']
1527 elif node
.has_key('refname'):
1528 href
= self
.document
.nameids
[node
['refname']]
1529 format
= self
.settings
.footnote_references
1530 if format
== 'brackets':
1532 self
.context
.append(']')
1533 elif format
== 'superscript':
1534 suffix
= '\\raisebox{.5em}[0em]{\\scriptsize'
1535 self
.context
.append('}')
1536 else: # shouldn't happen
1537 raise AssertionError('Illegal footnote reference format.')
1538 self
.body
.append('%s\\hyperlink{%s}{' % (suffix
,href
))
1540 def depart_footnote_reference(self
, node
):
1541 if self
.use_latex_footnotes
:
1543 self
.body
.append('}%s' % self
.context
.pop())
1545 # footnote/citation label
1546 def label_delim(self
, node
, bracket
, superscript
):
1547 if isinstance(node
.parent
, nodes
.footnote
):
1548 if self
.use_latex_footnotes
:
1549 raise nodes
.SkipNode
1550 if self
.settings
.footnote_references
== 'brackets':
1551 self
.body
.append(bracket
)
1553 self
.body
.append(superscript
)
1555 assert isinstance(node
.parent
, nodes
.citation
)
1556 if not self
._use
_latex
_citations
:
1557 self
.body
.append(bracket
)
1559 def visit_label(self
, node
):
1560 self
.label_delim(node
, '[', '$^{')
1562 def depart_label(self
, node
):
1563 self
.label_delim(node
, ']', '}$')
1565 # elements generated by the framework e.g. section numbers.
1566 def visit_generated(self
, node
):
1569 def depart_generated(self
, node
):
1572 def visit_header(self
, node
):
1573 self
.context
.append(len(self
.body
))
1575 def depart_header(self
, node
):
1576 start
= self
.context
.pop()
1577 self
.body_prefix
.append('\n\\verb|begin_header|\n')
1578 self
.body_prefix
.extend(self
.body
[start
:])
1579 self
.body_prefix
.append('\n\\verb|end_header|\n')
1580 del self
.body
[start
:]
1582 def visit_hint(self
, node
):
1583 self
.visit_admonition(node
, 'hint')
1585 def depart_hint(self
, node
):
1586 self
.depart_admonition()
1588 def latex_image_length(self
, width_str
):
1589 match
= re
.match('(\d*\.?\d*)\s*(\S*)', width_str
)
1594 amount
, unit
= match
.groups()[:2]
1596 # LaTeX does not know pixels but points
1597 res
= "%spt" % amount
1599 res
= "%.3f\\linewidth" % (float(amount
)/100.0)
1602 def visit_image(self
, node
):
1603 attrs
= node
.attributes
1604 # Add image URI to dependency list, assuming that it's
1605 # referring to a local file.
1606 self
.settings
.record_dependencies
.add(attrs
['uri'])
1607 pre
= [] # in reverse order
1609 include_graphics_options
= []
1610 inline
= isinstance(node
.parent
, nodes
.TextElement
)
1611 if attrs
.has_key('scale'):
1612 # Could also be done with ``scale`` option to
1613 # ``\includegraphics``; doing it this way for consistency.
1614 pre
.append('\\scalebox{%f}{' % (attrs
['scale'] / 100.0,))
1616 if attrs
.has_key('width'):
1617 include_graphics_options
.append('width=%s' % (
1618 self
.latex_image_length(attrs
['width']), ))
1619 if attrs
.has_key('height'):
1620 include_graphics_options
.append('height=%s' % (
1621 self
.latex_image_length(attrs
['height']), ))
1622 if attrs
.has_key('align'):
1624 # By default latex aligns the top of an image.
1625 (1, 'top'): ('', ''),
1626 (1, 'middle'): ('\\raisebox{-0.5\\height}{', '}'),
1627 (1, 'bottom'): ('\\raisebox{-\\height}{', '}'),
1628 (0, 'center'): ('{\\hfill', '\\hfill}'),
1629 # These 2 don't exactly do the right thing. The image should
1630 # be floated alongside the paragraph. See
1631 # http://www.w3.org/TR/html4/struct/objects.html#adef-align-IMG
1632 (0, 'left'): ('{', '\\hfill}'),
1633 (0, 'right'): ('{\\hfill', '}'),}
1635 pre
.append(align_prepost
[inline
, attrs
['align']][0])
1636 post
.append(align_prepost
[inline
, attrs
['align']][1])
1638 pass # XXX complain here?
1643 self
.body
.extend( pre
)
1645 if len(include_graphics_options
)>0:
1646 options
= '[%s]' % (','.join(include_graphics_options
))
1647 self
.body
.append( '\\includegraphics%s{%s}' % (
1648 options
, attrs
['uri'] ) )
1649 self
.body
.extend( post
)
1651 def depart_image(self
, node
):
1654 def visit_important(self
, node
):
1655 self
.visit_admonition(node
, 'important')
1657 def depart_important(self
, node
):
1658 self
.depart_admonition()
1660 def visit_interpreted(self
, node
):
1661 # @@@ Incomplete, pending a proper implementation on the
1662 # Parser/Reader end.
1663 self
.visit_literal(node
)
1665 def depart_interpreted(self
, node
):
1666 self
.depart_literal(node
)
1668 def visit_legend(self
, node
):
1669 self
.body
.append('{\\small ')
1671 def depart_legend(self
, node
):
1672 self
.body
.append('}')
1674 def visit_line(self
, node
):
1675 self
.body
.append('\item[] ')
1677 def depart_line(self
, node
):
1678 self
.body
.append('\n')
1680 def visit_line_block(self
, node
):
1681 if isinstance(node
.parent
, nodes
.line_block
):
1682 self
.body
.append('\\item[] \n'
1683 '\\begin{lineblock}{\\lineblockindentation}\n')
1685 self
.body
.append('\n\\begin{lineblock}{0em}\n')
1687 def depart_line_block(self
, node
):
1688 self
.body
.append('\\end{lineblock}\n')
1690 def visit_list_item(self
, node
):
1691 # Append "{}" in case the next character is "[", which would break
1692 # LaTeX's list environment (no numbering and the "[" is not printed).
1693 self
.body
.append('\\item {} ')
1695 def depart_list_item(self
, node
):
1696 self
.body
.append('\n')
1698 def visit_literal(self
, node
):
1700 self
.body
.append('\\texttt{')
1702 def depart_literal(self
, node
):
1703 self
.body
.append('}')
1706 def visit_literal_block(self
, node
):
1708 Render a literal-block.
1710 Literal blocks are used for "::"-prefixed literal-indented
1711 blocks of text, where the inline markup is not recognized,
1712 but are also the product of the parsed-literal directive,
1713 where the markup is respected.
1715 # In both cases, we want to use a typewriter/monospaced typeface.
1716 # For "real" literal-blocks, we can use \verbatim, while for all
1717 # the others we must use \mbox.
1719 # We can distinguish between the two kinds by the number of
1720 # siblings that compose this node: if it is composed by a
1721 # single element, it's surely either a real one or a
1722 # parsed-literal that does not contain any markup.
1724 if not self
.active_table
.is_open():
1725 # no quote inside tables, to avoid vertical space between
1726 # table border and literal block.
1727 # BUG: fails if normal text preceeds the literal block.
1728 self
.body
.append('\\begin{quote}')
1729 self
.context
.append('\\end{quote}\n')
1731 self
.body
.append('\n')
1732 self
.context
.append('\n')
1733 if (self
.settings
.use_verbatim_when_possible
and (len(node
) == 1)
1734 # in case of a parsed-literal containing just a "**bold**" word:
1735 and isinstance(node
[0], nodes
.Text
)):
1737 self
.body
.append('\\begin{verbatim}\n')
1739 self
.literal_block
= 1
1740 self
.insert_none_breaking_blanks
= 1
1741 self
.body
.append('{\\ttfamily \\raggedright \\noindent\n')
1742 # * obey..: is from julien and never worked for me (grubert).
1743 # self.body.append('{\\obeylines\\obeyspaces\\ttfamily\n')
1745 def depart_literal_block(self
, node
):
1747 self
.body
.append('\n\\end{verbatim}')
1750 self
.body
.append('\n}')
1751 self
.insert_none_breaking_blanks
= 0
1752 self
.literal_block
= 0
1753 # obey end: self.body.append('}\n')
1754 self
.body
.append(self
.context
.pop())
1756 def visit_meta(self
, node
):
1757 self
.body
.append('[visit_meta]\n')
1758 # BUG maybe set keywords for pdf
1759 ##self.head.append(self.starttag(node, 'meta', **node.attributes))
1761 def depart_meta(self
, node
):
1762 self
.body
.append('[depart_meta]\n')
1764 def visit_note(self
, node
):
1765 self
.visit_admonition(node
, 'note')
1767 def depart_note(self
, node
):
1768 self
.depart_admonition()
1770 def visit_option(self
, node
):
1771 if self
.context
[-1]:
1772 # this is not the first option
1773 self
.body
.append(', ')
1775 def depart_option(self
, node
):
1776 # flag tha the first option is done.
1777 self
.context
[-1] += 1
1779 def visit_option_argument(self
, node
):
1780 """The delimiter betweeen an option and its argument."""
1781 self
.body
.append(node
.get('delimiter', ' '))
1783 def depart_option_argument(self
, node
):
1786 def visit_option_group(self
, node
):
1787 self
.body
.append('\\item [')
1788 # flag for first option
1789 self
.context
.append(0)
1791 def depart_option_group(self
, node
):
1792 self
.context
.pop() # the flag
1793 self
.body
.append('] ')
1795 def visit_option_list(self
, node
):
1796 self
.body
.append('\\begin{optionlist}{3cm}\n')
1798 def depart_option_list(self
, node
):
1799 self
.body
.append('\\end{optionlist}\n')
1801 def visit_option_list_item(self
, node
):
1804 def depart_option_list_item(self
, node
):
1807 def visit_option_string(self
, node
):
1808 ##self.body.append(self.starttag(node, 'span', '', CLASS='option'))
1811 def depart_option_string(self
, node
):
1812 ##self.body.append('</span>')
1815 def visit_organization(self
, node
):
1816 self
.visit_docinfo_item(node
, 'organization')
1818 def depart_organization(self
, node
):
1819 self
.depart_docinfo_item(node
)
1821 def visit_paragraph(self
, node
):
1822 index
= node
.parent
.index(node
)
1823 if not ('contents' in self
.topic_classes
or
1824 (isinstance(node
.parent
, nodes
.compound
) and
1826 not isinstance(node
.parent
[index
- 1], nodes
.paragraph
) and
1827 not isinstance(node
.parent
[index
- 1], nodes
.compound
))):
1828 self
.body
.append('\n')
1830 def depart_paragraph(self
, node
):
1831 self
.body
.append('\n')
1833 def visit_problematic(self
, node
):
1834 self
.body
.append('{\\color{red}\\bfseries{}')
1836 def depart_problematic(self
, node
):
1837 self
.body
.append('}')
1839 def visit_raw(self
, node
):
1840 if 'latex' in node
.get('format', '').split():
1841 self
.body
.append(node
.astext())
1842 raise nodes
.SkipNode
1844 def visit_reference(self
, node
):
1845 # BUG: hash_char "#" is trouble some in LaTeX.
1846 # mbox and other environment do not like the '#'.
1848 if node
.has_key('refuri'):
1849 href
= node
['refuri'].replace('#',hash_char
)
1850 elif node
.has_key('refid'):
1851 href
= hash_char
+ node
['refid']
1852 elif node
.has_key('refname'):
1853 href
= hash_char
+ self
.document
.nameids
[node
['refname']]
1855 raise AssertionError('Unknown reference.')
1856 self
.body
.append('\\href{%s}{' % href
)
1857 if self
._reference
_label
and not node
.has_key('refuri'):
1858 self
.body
.append('\\%s{%s}}' % (self
._reference
_label
,
1859 href
.replace(hash_char
, '')))
1860 raise nodes
.SkipNode
1862 def depart_reference(self
, node
):
1863 self
.body
.append('}')
1865 def visit_revision(self
, node
):
1866 self
.visit_docinfo_item(node
, 'revision')
1868 def depart_revision(self
, node
):
1869 self
.depart_docinfo_item(node
)
1871 def visit_section(self
, node
):
1872 self
.section_level
+= 1
1873 # Initialize counter for potential subsections:
1874 self
._section
_number
.append(0)
1875 # Counter for this section's level (initialized by parent section):
1876 self
._section
_number
[self
.section_level
- 1] += 1
1878 def depart_section(self
, node
):
1879 # Remove counter for potential subsections:
1880 self
._section
_number
.pop()
1881 self
.section_level
-= 1
1883 def visit_sidebar(self
, node
):
1884 # BUG: this is just a hack to make sidebars render something
1885 self
.body
.append('\n\\setlength{\\locallinewidth}{0.9\\admonitionwidth}\n')
1886 self
.body
.append('\\begin{center}\\begin{sffamily}\n')
1887 self
.body
.append('\\fbox{\\colorbox[gray]{0.80}{\\parbox{\\admonitionwidth}{\n')
1889 def depart_sidebar(self
, node
):
1890 self
.body
.append('}}}\n') # end parbox colorbox fbox
1891 self
.body
.append('\\end{sffamily}\n\\end{center}\n');
1892 self
.body
.append('\n\\setlength{\\locallinewidth}{\\linewidth}\n')
1895 attribution_formats
= {'dash': ('---', ''),
1896 'parentheses': ('(', ')'),
1897 'parens': ('(', ')'),
1900 def visit_attribution(self
, node
):
1901 prefix
, suffix
= self
.attribution_formats
[self
.settings
.attribution
]
1902 self
.body
.append('\n\\begin{flushright}\n')
1903 self
.body
.append(prefix
)
1904 self
.context
.append(suffix
)
1906 def depart_attribution(self
, node
):
1907 self
.body
.append(self
.context
.pop() + '\n')
1908 self
.body
.append('\\end{flushright}\n')
1910 def visit_status(self
, node
):
1911 self
.visit_docinfo_item(node
, 'status')
1913 def depart_status(self
, node
):
1914 self
.depart_docinfo_item(node
)
1916 def visit_strong(self
, node
):
1917 self
.body
.append('\\textbf{')
1918 self
.literal_block_stack
.append('\\textbf{')
1920 def depart_strong(self
, node
):
1921 self
.body
.append('}')
1922 self
.literal_block_stack
.pop()
1924 def visit_substitution_definition(self
, node
):
1925 raise nodes
.SkipNode
1927 def visit_substitution_reference(self
, node
):
1928 self
.unimplemented_visit(node
)
1930 def visit_subtitle(self
, node
):
1931 if isinstance(node
.parent
, nodes
.sidebar
):
1932 self
.body
.append('~\\\\\n\\textbf{')
1933 self
.context
.append('}\n\\smallskip\n')
1934 elif isinstance(node
.parent
, nodes
.document
):
1935 self
.title
= self
.title
+ \
1936 '\\\\\n\\large{%s}\n' % self
.encode(node
.astext())
1937 raise nodes
.SkipNode
1938 elif isinstance(node
.parent
, nodes
.section
):
1939 self
.body
.append('\\textbf{')
1940 self
.context
.append('}\\vspace{0.2cm}\n\n\\noindent ')
1942 def depart_subtitle(self
, node
):
1943 self
.body
.append(self
.context
.pop())
1945 def visit_system_message(self
, node
):
1948 def depart_system_message(self
, node
):
1949 self
.body
.append('\n')
1951 def visit_table(self
, node
):
1952 if self
.active_table
.is_open():
1953 self
.table_stack
.append(self
.active_table
)
1954 # nesting longtable does not work (e.g. 2007-04-18)
1955 self
.active_table
= Table('tabular',self
.settings
.table_style
)
1956 self
.active_table
.open()
1957 for cl
in node
['classes']:
1958 self
.active_table
.set_table_style(cl
)
1959 self
.body
.append('\n' + self
.active_table
.get_opening())
1961 def depart_table(self
, node
):
1962 self
.body
.append(self
.active_table
.get_closing() + '\n')
1963 self
.active_table
.close()
1964 if len(self
.table_stack
)>0:
1965 self
.active_table
= self
.table_stack
.pop()
1967 self
.active_table
.set_table_style(self
.settings
.table_style
)
1969 def visit_target(self
, node
):
1970 # BUG: why not (refuri or refid or refname) means not footnote ?
1971 if not (node
.has_key('refuri') or node
.has_key('refid')
1972 or node
.has_key('refname')):
1973 for id in node
['ids']:
1974 self
.body
.append('\\hypertarget{%s}{' % id)
1975 self
.context
.append('}' * len(node
['ids']))
1976 elif node
.get("refid"):
1977 self
.body
.append('\\hypertarget{%s}{' % node
.get("refid"))
1978 self
.context
.append('}')
1980 self
.context
.append('')
1982 def depart_target(self
, node
):
1983 self
.body
.append(self
.context
.pop())
1985 def visit_tbody(self
, node
):
1986 # BUG write preamble if not yet done (colspecs not [])
1987 # for tables without heads.
1988 if not self
.active_table
.get('preamble written'):
1989 self
.visit_thead(None)
1990 # self.depart_thead(None)
1992 def depart_tbody(self
, node
):
1995 def visit_term(self
, node
):
1996 self
.body
.append('\\item[{')
1998 def depart_term(self
, node
):
1999 # definition list term.
2000 # \leavevmode results in a line break if the term is followed by a item list.
2001 self
.body
.append('}] \leavevmode ')
2003 def visit_tgroup(self
, node
):
2004 #self.body.append(self.starttag(node, 'colgroup'))
2005 #self.context.append('</colgroup>\n')
2008 def depart_tgroup(self
, node
):
2011 def visit_thead(self
, node
):
2012 self
.body
.append('{%s}\n' % self
.active_table
.get_colspecs())
2013 if self
.active_table
.caption
:
2014 self
.body
.append('\\caption{%s}\\\\\n' % self
.active_table
.caption
)
2015 self
.active_table
.set('preamble written',1)
2016 # TODO longtable supports firsthead and lastfoot too.
2017 self
.body
.extend(self
.active_table
.visit_thead())
2019 def depart_thead(self
, node
):
2020 # the table header written should be on every page
2022 self
.body
.extend(self
.active_table
.depart_thead())
2023 # and the firsthead => \endfirsthead
2024 # BUG i want a "continued from previous page" on every not
2025 # firsthead, but then we need the header twice.
2027 # there is a \endfoot and \endlastfoot too.
2028 # but we need the number of columns to
2029 # self.body.append('\\multicolumn{%d}{c}{"..."}\n' % number_of_columns)
2030 # self.body.append('\\hline\n\\endfoot\n')
2031 # self.body.append('\\hline\n')
2032 # self.body.append('\\endlastfoot\n')
2034 def visit_tip(self
, node
):
2035 self
.visit_admonition(node
, 'tip')
2037 def depart_tip(self
, node
):
2038 self
.depart_admonition()
2040 def bookmark(self
, node
):
2041 """Append latex href and pdfbookmarks for titles.
2043 if node
.parent
['ids']:
2044 for id in node
.parent
['ids']:
2045 self
.body
.append('\\hypertarget{%s}{}\n' % id)
2046 if not self
.use_latex_toc
:
2047 # BUG level depends on style. pdflatex allows level 0 to 3
2048 # ToC would be the only on level 0 so i choose to decrement the rest.
2049 # "Table of contents" bookmark to see the ToC. To avoid this
2050 # we set all zeroes to one.
2051 l
= self
.section_level
2054 # pdftex does not like "_" subscripts in titles
2055 text
= self
.encode(node
.astext())
2056 for id in node
.parent
['ids']:
2057 self
.body
.append('\\pdfbookmark[%d]{%s}{%s}\n' % \
2060 def visit_title(self
, node
):
2061 """Section and other titles."""
2063 if isinstance(node
.parent
, nodes
.topic
):
2064 # the table of contents.
2066 if ('contents' in self
.topic_classes
2067 and self
.use_latex_toc
):
2068 self
.body
.append('\\renewcommand{\\contentsname}{')
2069 self
.context
.append('}\n\\tableofcontents\n\n\\bigskip\n')
2070 elif ('abstract' in self
.topic_classes
2071 and self
.settings
.use_latex_abstract
):
2072 raise nodes
.SkipNode
2073 else: # or section titles before the table of contents.
2074 # BUG: latex chokes on center environment with
2075 # "perhaps a missing item", therefore we use hfill.
2076 self
.body
.append('\\subsubsection*{~\\hfill ')
2077 # the closing brace for subsection.
2078 self
.context
.append('\\hfill ~}\n')
2079 # TODO: for admonition titles before the first section
2080 # either specify every possible node or ... ?
2081 elif isinstance(node
.parent
, nodes
.sidebar
) \
2082 or isinstance(node
.parent
, nodes
.admonition
):
2083 self
.body
.append('\\textbf{\\large ')
2084 self
.context
.append('}\n\\smallskip\n')
2085 elif isinstance(node
.parent
, nodes
.table
):
2086 # caption must be written after column spec
2087 self
.active_table
.caption
= self
.encode(node
.astext())
2088 raise nodes
.SkipNode
2089 elif self
.section_level
== 0:
2091 self
.title
= self
.encode(node
.astext())
2092 if not self
.pdfinfo
== None:
2093 self
.pdfinfo
.append( 'pdftitle={%s}' % self
.encode(node
.astext()) )
2094 raise nodes
.SkipNode
2096 self
.body
.append('\n\n')
2097 self
.body
.append('%' + '_' * 75)
2098 self
.body
.append('\n\n')
2101 if self
.use_latex_toc
:
2106 section_name
= self
.d_class
.section(self
.section_level
)
2107 self
.body
.append('\\%s%s{' % (section_name
, section_star
))
2108 # MAYBE postfix paragraph and subparagraph with \leavemode to
2109 # ensure floatables stay in the section and text starts on a new line.
2110 self
.context
.append('}\n')
2112 def depart_title(self
, node
):
2113 self
.body
.append(self
.context
.pop())
2114 for id in node
.parent
['ids']:
2115 self
.body
.append('\\label{%s}\n' % id)
2117 def visit_topic(self
, node
):
2118 self
.topic_classes
= node
['classes']
2119 if ('abstract' in self
.topic_classes
2120 and self
.settings
.use_latex_abstract
):
2121 self
.body
.append('\\begin{abstract}\n')
2123 def depart_topic(self
, node
):
2124 if ('abstract' in self
.topic_classes
2125 and self
.settings
.use_latex_abstract
):
2126 self
.body
.append('\\end{abstract}\n')
2127 self
.topic_classes
= []
2128 if 'contents' in node
['classes'] and self
.use_latex_toc
:
2131 self
.body
.append('\n')
2133 def visit_inline(self
, node
): # titlereference
2134 classes
= node
.get('classes', ['Unknown', ])
2136 self
.body
.append( '\\docutilsrole%s{' % cls
)
2137 self
.context
.append('}'*len(classes
))
2139 def depart_inline(self
, node
):
2140 self
.body
.append(self
.context
.pop())
2142 def visit_rubric(self
, node
):
2143 self
.body
.append('\\rubric{')
2144 self
.context
.append('}\n')
2146 def depart_rubric(self
, node
):
2147 self
.body
.append(self
.context
.pop())
2149 def visit_transition(self
, node
):
2150 self
.body
.append('\n\n')
2151 self
.body
.append('%' + '_' * 75)
2152 self
.body
.append('\n\\hspace*{\\fill}\\hrulefill\\hspace*{\\fill}')
2153 self
.body
.append('\n\n')
2155 def depart_transition(self
, node
):
2158 def visit_version(self
, node
):
2159 self
.visit_docinfo_item(node
, 'version')
2161 def depart_version(self
, node
):
2162 self
.depart_docinfo_item(node
)
2164 def visit_warning(self
, node
):
2165 self
.visit_admonition(node
, 'warning')
2167 def depart_warning(self
, node
):
2168 self
.depart_admonition()
2170 def unimplemented_visit(self
, node
):
2171 raise NotImplementedError('visiting unimplemented node type: %s'
2172 % node
.__class
__.__name
__)
2174 # def unknown_visit(self, node):
2175 # def default_visit(self, node):
2177 # vim: set ts=4 et ai :