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. ##.
21 from types
import ListType
22 from docutils
import frontend
, nodes
, languages
, writers
, utils
23 from docutils
.writers
.newlatex2e
import unicode_map
25 from docutils
.transforms
.references
import DanglingReferencesVisitor
27 class Writer(writers
.Writer
):
29 supported
= ('latex','latex2e')
30 """Formats this writer supports."""
33 'LaTeX-Specific Options',
34 'The LaTeX "--output-encoding" default is "latin-1:strict".',
35 (('Specify documentclass. Default is "article".',
37 {'default': 'article', }),
38 ('Specify document options. Multiple options can be given, '
39 'separated by commas. Default is "10pt,a4paper".',
40 ['--documentoptions'],
41 {'default': '10pt,a4paper', }),
42 ('Use LaTeX footnotes. LaTeX supports only numbered footnotes (does it?). '
43 'Default: no, uses figures.',
44 ['--use-latex-footnotes'],
45 {'default': 0, 'action': 'store_true',
46 'validator': frontend
.validate_boolean
}),
47 ('Format for footnote references: one of "superscript" or '
48 '"brackets". Default is "superscript".',
49 ['--footnote-references'],
50 {'choices': ['superscript', 'brackets'], 'default': 'superscript',
51 'metavar': '<format>',
52 'overrides': 'trim_footnote_reference_space'}),
53 ('Use LaTeX citations. '
54 'Default: no, uses figures which might get mixed with images.',
55 ['--use-latex-citations'],
56 {'default': 0, 'action': 'store_true',
57 'validator': frontend
.validate_boolean
}),
58 ('Format for block quote attributions: one of "dash" (em-dash '
59 'prefix), "parentheses"/"parens", or "none". Default is "dash".',
61 {'choices': ['dash', 'parentheses', 'parens', 'none'],
62 'default': 'dash', 'metavar': '<format>'}),
63 ('Specify a stylesheet file. The file will be "input" by latex in '
64 'the document header. Default is no stylesheet (""). '
65 'Overrides --stylesheet-path.',
67 {'default': '', 'metavar': '<file>',
68 'overrides': 'stylesheet_path'}),
69 ('Specify a stylesheet file, relative to the current working '
70 'directory. Overrides --stylesheet.',
71 ['--stylesheet-path'],
72 {'metavar': '<file>', 'overrides': 'stylesheet'}),
73 ('Embed the stylesheet in the output LaTeX file. The stylesheet '
74 'file must be accessible during processing (--stylesheet-path is '
75 'recommended). This is not set by default.',
76 ['--embed-stylesheet'],
77 {'default': 0, 'action': 'store_true',
78 'validator': frontend
.validate_boolean
}),
79 ('Table of contents by docutils (default) or LaTeX. LaTeX (writer) '
80 'supports only one ToC per document, but docutils does not know of '
81 'pagenumbers. LaTeX table of contents also means LaTeX generates '
84 {'default': 0, 'action': 'store_true',
85 'validator': frontend
.validate_boolean
}),
86 ('Add parts on top of the section hierarchy.',
87 ['--use-part-section'],
88 {'default': 0, 'action': 'store_true',
89 'validator': frontend
.validate_boolean
}),
90 ('Let LaTeX print author and date, do not show it in docutils '
92 ['--use-latex-docinfo'],
93 {'default': 0, 'action': 'store_true',
94 'validator': frontend
.validate_boolean
}),
95 ('Use LaTeX abstract environment for the documents abstract.'
96 'Per default the abstract is an unnumbered section.',
97 ['--use-latex-abstract'],
98 {'default': 0, 'action': 'store_true',
99 'validator': frontend
.validate_boolean
}),
100 ('Color of any hyperlinks embedded in text '
101 '(default: "blue", "0" to disable).',
102 ['--hyperlink-color'], {'default': 'blue'}),
103 ('Enable compound enumerators for nested enumerated lists '
104 '(e.g. "1.2.a.ii"). Default: disabled.',
105 ['--compound-enumerators'],
106 {'default': None, 'action': 'store_true',
107 'validator': frontend
.validate_boolean
}),
108 ('Disable compound enumerators for nested enumerated lists. This is '
110 ['--no-compound-enumerators'],
111 {'action': 'store_false', 'dest': 'compound_enumerators'}),
112 ('Enable section ("." subsection ...) prefixes for compound '
113 'enumerators. This has no effect without --compound-enumerators. '
114 'Default: disabled.',
115 ['--section-prefix-for-enumerators'],
116 {'default': None, 'action': 'store_true',
117 'validator': frontend
.validate_boolean
}),
118 ('Disable section prefixes for compound enumerators. '
119 'This is the default.',
120 ['--no-section-prefix-for-enumerators'],
121 {'action': 'store_false', 'dest': 'section_prefix_for_enumerators'}),
122 ('Set the separator between section number and enumerator '
123 'for compound enumerated lists. Default is "-".',
124 ['--section-enumerator-separator'],
125 {'default': '-', 'metavar': '<char>'}),
126 ('When possibile, use the specified environment for literal-blocks. '
127 'Default is quoting of whitespace and special chars.',
128 ['--literal-block-env'],
130 ('When possibile, use verbatim for literal-blocks. '
131 'Compatibility alias for "--literal-block-env=verbatim".',
132 ['--use-verbatim-when-possible'],
133 {'default': 0, 'action': 'store_true',
134 'validator': frontend
.validate_boolean
}),
135 ('Table style. "standard" with horizontal and vertical lines, '
136 '"booktabs" (LaTeX booktabs style) only horizontal lines '
137 'above and below the table and below the header or "nolines". '
138 'Default: "standard"',
140 {'choices': ['standard', 'booktabs','nolines'], 'default': 'standard',
141 'metavar': '<format>'}),
142 ('LaTeX graphicx package option. '
143 'Possible values are "dvips", "pdftex". "auto" includes LaTeX code '
144 'to use "pdftex" if processing with pdf(la)tex and dvips otherwise. '
145 'Default is no option.',
146 ['--graphicx-option'],
148 ('LaTeX font encoding. '
149 'Possible values are "T1", "OT1", "" or some other fontenc option. '
150 'The font encoding influences available symbols, e.g. "<<" as one '
151 'character. Default is "" which leads to package "ae" (a T1 '
152 'emulation using CM fonts).',
155 ('Per default the latex-writer puts the reference title into '
156 'hyperreferences. Specify "ref*" or "pageref*" to get the section '
157 'number or the page number.',
158 ['--reference-label'],
159 {'default': None, }),
160 ('Specify style and database for bibtex, for example '
161 '"--use-bibtex=mystyle,mydb1,mydb2".',
163 {'default': None, }),
166 settings_defaults
= {'output_encoding': 'latin-1'}
168 relative_path_settings
= ('stylesheet_path',)
170 config_section
= 'latex2e writer'
171 config_section_dependencies
= ('writers',)
173 visitor_attributes
= ("head_prefix", "head",
174 "body_prefix", "body", "body_suffix")
177 """Final translated form of `document`."""
180 writers
.Writer
.__init
__(self
)
181 self
.translator_class
= LaTeXTranslator
184 visitor
= self
.translator_class(self
.document
)
185 self
.document
.walkabout(visitor
)
186 self
.output
= visitor
.astext()
188 for attr
in self
.visitor_attributes
:
189 setattr(self
, attr
, getattr(visitor
, attr
))
191 def assemble_parts(self
):
192 writers
.Writer
.assemble_parts(self
)
193 for part
in self
.visitor_attributes
:
194 self
.parts
[part
] = ''.join(getattr(self
, part
))
201 * LaTeX does not support multiple tocs in one document.
202 (might be no limitation except for docutils documentation)
204 The "minitoc" latex package can produce per-chapter tocs in
205 book and report document classes.
209 * linewidth - width of a line in the local environment
210 * textwidth - the width of text on the page
212 Maybe always use linewidth ?
214 *Bug* inside a minipage a (e.g. Sidebar) the linewidth is
215 not changed, needs fix in docutils so that tables
218 So we add locallinewidth set it initially and
219 on entering sidebar and reset on exit.
223 """Language specifics for LaTeX."""
224 # country code by a.schlock.
225 # partly manually converted from iso and babel stuff, dialects and some
227 'no': 'norsk', #XXX added by hand ( forget about nynorsk?)
228 'gd': 'scottish', #XXX added by hand
229 'hu': 'magyar', #XXX added by hand
230 'pt': 'portuguese',#XXX added by hand
240 # french, francais, canadien, acadian
241 'de': 'ngerman', #XXX rather than german
242 # ngerman, naustrian, german, germanb, austrian
245 # english, USenglish, american, UKenglish, british, canadian
273 def __init__(self
,lang
):
275 # pdflatex does not produce double quotes for ngerman in tt.
276 self
.double_quote_replacment
= None
277 if re
.search('^de',self
.language
):
278 #self.quotes = ("\"`", "\"'")
279 self
.quotes
= ('{\\glqq}', '{\\grqq}')
280 self
.double_quote_replacment
= "{\\dq}"
281 elif re
.search('^it',self
.language
):
282 self
.quotes
= ("``", "''")
283 self
.double_quote_replacment
= r
'{\char`\"}'
285 self
.quotes
= ("``", "''")
287 # for spanish ``~n`` must be ``~{}n``
288 if re
.search('^es',self
.language
):
293 def next_quote(self
):
294 q
= self
.quotes
[self
.quote_index
]
295 self
.quote_index
= (self
.quote_index
+1)%2
298 def quote_quotes(self
,text
):
300 for part
in text
.split('"'):
304 t
+= self
.next_quote() + part
307 def double_quotes_in_tt (self
,text
):
308 if not self
.double_quote_replacment
:
310 return text
.replace('"', self
.double_quote_replacment
)
312 def get_language(self
):
313 if self
.language
in self
._ISO
639_TO
_BABEL
:
314 return self
._ISO
639_TO
_BABEL
[self
.language
]
317 lang
= self
.language
.split("_")[0]
318 if lang
in self
._ISO
639_TO
_BABEL
:
319 return self
._ISO
639_TO
_BABEL
[lang
]
323 'optionlist_environment' : [
324 '\\newcommand{\\optionlistlabel}[1]{\\bf #1 \\hfill}\n'
325 '\\newenvironment{optionlist}[1]\n'
327 ' {\\setlength{\\labelwidth}{#1}\n'
328 ' \\setlength{\\rightmargin}{1cm}\n'
329 ' \\setlength{\\leftmargin}{\\rightmargin}\n'
330 ' \\addtolength{\\leftmargin}{\\labelwidth}\n'
331 ' \\addtolength{\\leftmargin}{\\labelsep}\n'
332 ' \\renewcommand{\\makelabel}{\\optionlistlabel}}\n'
335 'lineblock_environment' : [
336 '\\newlength{\\lineblockindentation}\n'
337 '\\setlength{\\lineblockindentation}{2.5em}\n'
338 '\\newenvironment{lineblock}[1]\n'
340 ' {\\setlength{\\partopsep}{\\parskip}\n'
341 ' \\addtolength{\\partopsep}{\\baselineskip}\n'
342 ' \\topsep0pt\\itemsep0.15\\baselineskip\\parsep0pt\n'
347 'footnote_floats' : [
348 '% begin: floats for footnotes tweaking.\n',
349 '\\setlength{\\floatsep}{0.5em}\n',
350 '\\setlength{\\textfloatsep}{\\fill}\n',
351 '\\addtolength{\\textfloatsep}{3em}\n',
352 '\\renewcommand{\\textfraction}{0.5}\n',
353 '\\renewcommand{\\topfraction}{0.5}\n',
354 '\\renewcommand{\\bottomfraction}{0.5}\n',
355 '\\setcounter{totalnumber}{50}\n',
356 '\\setcounter{topnumber}{50}\n',
357 '\\setcounter{bottomnumber}{50}\n',
358 '% end floats for footnotes\n',
361 '% some commands, that could be overwritten in the style file.\n'
362 '\\newcommand{\\rubric}[1]'
363 '{\\subsection*{~\\hfill {\\it #1} \\hfill ~}}\n'
364 '\\newcommand{\\titlereference}[1]{\\textsl{#1}}\n'
365 '% end of "some commands"\n',
370 """Details of a LaTeX document class."""
372 def __init__(self
, document_class
, with_part
=False):
373 self
.document_class
= document_class
374 self
._with
_part
= with_part
376 def section(self
, level
):
377 """ Return the section name at the given level for the specific
380 Level is 1,2,3..., as level 0 is the title."""
382 sections
= [ 'section', 'subsection', 'subsubsection',
383 'paragraph', 'subparagraph' ]
384 if self
.document_class
in ('book', 'report', 'scrreprt', 'scrbook'):
385 sections
.insert(0, 'chapter')
387 sections
.insert(0, 'part')
388 if level
<= len(sections
):
389 return sections
[level
-1]
394 """ Manage a table while traversing.
395 Maybe change to a mixin defining the visit/departs, but then
396 class Table internal variables are in the Translator.
400 * standard: horizontal and vertical lines
401 * booktabs (requires booktabs latex package): only horizontal lines
402 * nolines, borderless : no lines
404 def __init__(self
,translator
,latex_type
,table_style
):
405 self
._translator
= translator
406 self
._latex
_type
= latex_type
407 self
._table
_style
= table_style
409 # miscellaneous attributes
421 self
._in
_head
= 0 # maybe context with search
424 self
._col
_specs
= None
431 def set_table_style(self
, table_style
):
432 if not table_style
in ('standard','booktabs','borderless','nolines'):
434 self
._table
_style
= table_style
436 def used_packages(self
):
437 if self
._table
_style
== 'booktabs':
438 return '\\usepackage{booktabs}\n'
440 def get_latex_type(self
):
441 return self
._latex
_type
443 def set(self
,attr
,value
):
444 self
._attrs
[attr
] = value
446 if attr
in self
._attrs
:
447 return self
._attrs
[attr
]
449 def get_vertical_bar(self
):
450 if self
._table
_style
== 'standard':
453 # horizontal lines are drawn below a row, because we.
454 def get_opening(self
):
455 if self
._latex
_type
== 'longtable':
456 # otherwise longtable might move before paragraph and subparagraph
457 prefix
= '\\leavevmode\n'
460 return '%s\\begin{%s}[c]' % (prefix
, self
._latex
_type
)
461 def get_closing(self
):
463 if self
._table
_style
== 'booktabs':
464 line
= '\\bottomrule\n'
465 elif self
._table
_style
== 'standard':
467 return '%s\\end{%s}' % (line
,self
._latex
_type
)
469 def visit_colspec(self
, node
):
470 self
._col
_specs
.append(node
)
471 # "stubs" list is an attribute of the tgroup element:
472 self
.stubs
.append(node
.attributes
.get('stub'))
474 def get_colspecs(self
):
476 Return column specification for longtable.
478 Assumes reST line length being 80 characters.
479 Table width is hairy.
485 usually gets to narrow, therefore we add 1 (fiddlefactor).
490 # first see if we get too wide.
491 for node
in self
._col
_specs
:
492 colwidth
= float(node
['colwidth']+1) / width
493 total_width
+= colwidth
496 # donot make it full linewidth
498 if total_width
> 1.0:
499 factor
/= total_width
500 bar
= self
.get_vertical_bar()
501 latex_table_spec
= ""
502 for node
in self
._col
_specs
:
503 colwidth
= factor
* float(node
['colwidth']+1) / width
504 self
._col
_width
.append(colwidth
+0.005)
505 self
._rowspan
.append(0)
506 latex_table_spec
+= "%sp{%.3f\\locallinewidth}" % (bar
,colwidth
+0.005)
507 return latex_table_spec
+bar
509 def get_column_width(self
):
510 """ return columnwidth for current cell (not multicell)
512 return "%.2f\\locallinewidth" % self
._col
_width
[self
._cell
_in
_row
-1]
514 def get_caption(self
):
517 if 1 == self
._translator
.thead_depth():
518 return '\\caption{%s}\\\\\n' % self
.caption
519 return '\\caption[]{%s (... continued)}\\\\\n' % self
.caption
521 def need_recurse(self
):
522 if self
._latex
_type
== 'longtable':
523 return 1 == self
._translator
.thead_depth()
526 def visit_thead(self
):
528 if self
._table
_style
== 'standard':
530 elif self
._table
_style
== 'booktabs':
531 return ['\\toprule\n']
533 def depart_thead(self
):
535 #if self._table_style == 'standard':
536 # a.append('\\hline\n')
537 if self
._table
_style
== 'booktabs':
538 a
.append('\\midrule\n')
539 if self
._latex
_type
== 'longtable':
540 if 1 == self
._translator
.thead_depth():
541 a
.append('\\endfirsthead\n')
543 a
.append('\\endhead\n')
544 a
.append('\\multicolumn{%d}{c}{\\hfill ... continued on next page} \\\\\n' % len(self
._col
_specs
))
545 a
.append('\\endfoot\n\\endlastfoot\n')
546 # for longtable one could add firsthead, foot and lastfoot
550 self
._cell
_in
_row
= 0
551 def depart_row(self
):
553 self
._cell
_in
_row
= None # remove cell counter
554 for i
in range(len(self
._rowspan
)):
555 if (self
._rowspan
[i
]>0):
556 self
._rowspan
[i
] -= 1
558 if self
._table
_style
== 'standard':
560 for i
in range(len(self
._rowspan
)):
561 if (self
._rowspan
[i
]<=0):
563 if len(rowspans
)==len(self
._rowspan
):
564 res
.append('\\hline\n')
571 c_start
= rowspans
.pop()
574 cline
+= '\\cline{%d-%d}\n' % (c_start
,c_start
)
578 def set_rowspan(self
,cell
,value
):
580 self
._rowspan
[cell
] = value
583 def get_rowspan(self
,cell
):
585 return self
._rowspan
[cell
]
588 def get_entry_number(self
):
589 return self
._cell
_in
_row
590 def visit_entry(self
):
591 self
._cell
_in
_row
+= 1
592 def is_stub_column(self
):
593 if len(self
.stubs
) >= self
._cell
_in
_row
:
594 return self
.stubs
[self
._cell
_in
_row
-1]
598 class LaTeXTranslator(nodes
.NodeVisitor
):
600 # When options are given to the documentclass, latex will pass them
601 # to other packages, as done with babel.
602 # Dummy settings might be taken from document settings
607 latex_head
= '\\documentclass[%s]{%s}\n'
608 linking
= "\\ifthenelse{\\isundefined{\\hypersetup}}{\n" \
609 +"\\usepackage[colorlinks=%s,linkcolor=%s,urlcolor=%s]{hyperref}\n" \
611 stylesheet
= '\\input{%s}\n'
612 # add a generated on day , machine by user using docutils version.
613 generator
= '% generated by Docutils <http://docutils.sourceforge.net/>\n'
614 # Config setting defaults
615 # -----------------------
617 # use latex tableofcontents or let docutils do it.
620 # TODO: use mixins for different implementations.
621 # list environment for docinfo. else tabularx
622 use_optionlist_for_docinfo
= 0 # NOT YET IN USE
624 # Use compound enumerations (1.A.1.)
625 compound_enumerators
= 0
627 # If using compound enumerations, include section information.
628 section_prefix_for_enumerators
= 0
630 # This is the character that separates the section ("." subsection ...)
631 # prefix from the regular list enumerator.
632 section_enumerator_separator
= '-'
635 hyperlink_color
= "blue"
637 def __init__(self
, document
):
638 nodes
.NodeVisitor
.__init
__(self
, document
)
639 self
.settings
= settings
= document
.settings
640 self
.latex_encoding
= self
.to_latex_encoding(settings
.output_encoding
)
641 self
.use_latex_toc
= settings
.use_latex_toc
642 self
.use_latex_docinfo
= settings
.use_latex_docinfo
643 self
.use_latex_footnotes
= settings
.use_latex_footnotes
644 self
._use
_latex
_citations
= settings
.use_latex_citations
645 self
._reference
_label
= settings
.reference_label
646 self
.hyperlink_color
= settings
.hyperlink_color
647 self
.compound_enumerators
= settings
.compound_enumerators
648 self
.font_encoding
= settings
.font_encoding
649 self
.section_prefix_for_enumerators
= (
650 settings
.section_prefix_for_enumerators
)
651 self
.section_enumerator_separator
= (
652 settings
.section_enumerator_separator
.replace('_', '\\_'))
653 if self
.hyperlink_color
== '0':
654 self
.hyperlink_color
= 'black'
655 self
.colorlinks
= 'false'
657 self
.colorlinks
= 'true'
659 if self
.settings
.use_bibtex
:
660 self
.bibtex
= self
.settings
.use_bibtex
.split(",",1)
661 # TODO avoid errors on not declared citations.
664 # language: labels, bibliographic_fields, and author_separators.
665 # to allow writing labes for specific languages.
666 self
.language
= languages
.get_language(settings
.language_code
)
667 self
.babel
= Babel(settings
.language_code
)
668 self
.author_separator
= self
.language
.author_separators
[0]
669 self
.d_options
= self
.settings
.documentoptions
670 if self
.babel
.get_language():
671 self
.d_options
+= ',%s' % self
.babel
.get_language()
672 self
.latex_equivalents
[u
'\u00A0'] = self
.babel
.nobr
674 self
.d_class
= DocumentClass(settings
.documentclass
,
675 settings
.use_part_section
)
676 # object for a table while proccessing.
677 self
.table_stack
= []
678 self
.active_table
= Table(self
,'longtable',settings
.table_style
)
680 # HACK. Should have more sophisticated typearea handling.
681 if settings
.documentclass
.find('scr') == -1:
682 self
.typearea
= '\\usepackage[DIV12]{typearea}\n'
684 if self
.d_options
.find('DIV') == -1 and self
.d_options
.find('BCOR') == -1:
685 self
.typearea
= '\\typearea{12}\n'
689 if self
.font_encoding
== 'OT1':
691 elif self
.font_encoding
== '':
692 fontenc_header
= '\\usepackage{ae}\n\\usepackage{aeguill}\n'
694 fontenc_header
= '\\usepackage[%s]{fontenc}\n' % (self
.font_encoding
,)
695 if self
.latex_encoding
.startswith('utf8'):
696 input_encoding
= '\\usepackage{ucs}\n\\usepackage[utf8x]{inputenc}\n'
698 input_encoding
= '\\usepackage[%s]{inputenc}\n' % self
.latex_encoding
699 if self
.settings
.graphicx_option
== '':
700 self
.graphicx_package
= '\\usepackage{graphicx}\n'
701 elif self
.settings
.graphicx_option
.lower() == 'auto':
702 self
.graphicx_package
= '\n'.join(
703 ('%Check if we are compiling under latex or pdflatex',
704 '\\ifx\\pdftexversion\\undefined',
705 ' \\usepackage{graphicx}',
707 ' \\usepackage[pdftex]{graphicx}',
710 self
.graphicx_package
= (
711 '\\usepackage[%s]{graphicx}\n' % self
.settings
.graphicx_option
)
714 self
.latex_head
% (self
.d_options
,self
.settings
.documentclass
),
715 '\\usepackage{babel}\n', # language is in documents settings.
717 '\\usepackage{shortvrb}\n', # allows verb in footnotes.
719 # * tabularx: for docinfo, automatic width of columns, always on one page.
720 '\\usepackage{tabularx}\n',
721 '\\usepackage{longtable}\n',
722 self
.active_table
.used_packages(),
723 # possible other packages.
725 # * ltxtable is a combination of tabularx and longtable (pagebreaks).
728 # extra space between text in tables and the line above them
729 '\\setlength{\\extrarowheight}{2pt}\n',
730 '\\usepackage{amsmath}\n', # what fore amsmath.
731 self
.graphicx_package
,
732 '\\usepackage{color}\n',
733 '\\usepackage{multirow}\n',
734 '\\usepackage{ifthen}\n', # before hyperref!
738 '\\newlength{\\admonitionwidth}\n',
739 '\\setlength{\\admonitionwidth}{0.9\\textwidth}\n'
740 # width for docinfo tablewidth
741 '\\newlength{\\docinfowidth}\n',
742 '\\setlength{\\docinfowidth}{0.9\\textwidth}\n'
743 # linewidth of current environment, so tables are not wider
744 # than the sidebar: using locallinewidth seems to defer evaluation
745 # of linewidth, this is fixing it.
746 '\\newlength{\\locallinewidth}\n',
749 self
.head_prefix
.extend( latex_headings
['optionlist_environment'] )
750 self
.head_prefix
.extend( latex_headings
['lineblock_environment'] )
751 self
.head_prefix
.extend( latex_headings
['footnote_floats'] )
752 self
.head_prefix
.extend( latex_headings
['some_commands'] )
753 ## stylesheet is last: so it might be possible to overwrite defaults.
754 stylesheet
= utils
.get_stylesheet_reference(settings
)
756 if settings
.embed_stylesheet
:
757 stylesheet
= utils
.get_stylesheet_reference(
758 settings
, os
.path
.join(os
.getcwd(), 'dummy'))
759 settings
.record_dependencies
.add(stylesheet
)
760 self
.head_prefix
.append(open(stylesheet
).read())
762 self
.head_prefix
.append(self
.stylesheet
% (stylesheet
))
763 # hyperref after stylesheet
764 # TODO conditionally if no hyperref is used dont include
765 self
.head_prefix
.append( self
.linking
% (
766 self
.colorlinks
, self
.hyperlink_color
, self
.hyperlink_color
))
769 if self
.settings
.literal_block_env
!= '':
770 self
.settings
.use_verbatim_when_possible
= True
771 if self
.linking
: # and maybe check for pdf
773 self
.pdfauthor
= None
774 # pdftitle, pdfsubject, pdfauthor, pdfkeywords,
775 # pdfcreator, pdfproducer
778 # NOTE: Latex wants a date and an author, rst puts this into
779 # docinfo, so normally we do not want latex author/date handling.
780 # latex article has its own handling of date and author, deactivate.
781 # self.astext() adds \title{...} \author{...} \date{...}, even if the
782 # "..." are empty strings.
784 # separate title, so we can appen subtitle.
786 # if use_latex_docinfo: collects lists of author/organization/contact/address lines
787 self
.author_stack
= []
790 self
.body_prefix
= ['\\raggedbottom\n']
792 self
.body_suffix
= ['\n']
793 self
.section_level
= 0
795 self
.topic_classes
= []
796 # column specification for tables
797 self
.table_caption
= None
801 # verbatim: to tell encode not to encode.
803 # insert_newline: to tell encode to replace blanks by "~".
804 self
.insert_none_breaking_blanks
= 0
805 # insert_newline: to tell encode to add latex newline.
806 self
.insert_newline
= 0
807 # mbox_newline: to tell encode to add mbox and newline.
808 self
.mbox_newline
= 0
809 # inside citation reference labels underscores dont need to be escaped.
810 self
.inside_citation_reference_label
= 0
812 # Stack of section counters so that we don't have to use_latex_toc.
813 # This will grow and shrink as processing occurs.
814 # Initialized for potential first-level sections.
815 self
._section
_number
= [0]
817 # The current stack of enumerations so that we can expand
818 # them into a compound enumeration.
819 self
._enumeration
_counters
= []
821 # The maximum number of enumeration counters we've used.
822 # If we go beyond this number, we need to create a new
823 # counter; otherwise, just reuse an old one.
824 self
._max
_enumeration
_counters
= 0
830 # inside literal block: no quote mangling.
831 self
.literal_block
= 0
832 self
.literal_block_stack
= []
834 # true when encoding in math mode
837 def to_latex_encoding(self
,docutils_encoding
):
839 Translate docutils encoding name into latex's.
841 Default fallback method is remove "-" and "_" chars from docutils_encoding.
844 tr
= { "iso-8859-1": "latin1", # west european
845 "iso-8859-2": "latin2", # east european
846 "iso-8859-3": "latin3", # esperanto, maltese
847 "iso-8859-4": "latin4", # north european,scandinavian, baltic
848 "iso-8859-5": "iso88595", # cyrillic (ISO)
849 "iso-8859-9": "latin5", # turkish
850 "iso-8859-15": "latin9", # latin9, update to latin1.
851 "mac_cyrillic": "maccyr", # cyrillic (on Mac)
852 "windows-1251": "cp1251", # cyrillic (on Windows)
853 "koi8-r": "koi8-r", # cyrillic (Russian)
854 "koi8-u": "koi8-u", # cyrillic (Ukrainian)
855 "windows-1250": "cp1250", #
856 "windows-1252": "cp1252", #
857 "us-ascii": "ascii", # ASCII (US)
858 # unmatched encodings
860 #"": "ansinew", # windows 3.1 ansi
861 #"": "ascii", # ASCII encoding for the range 32--127.
862 #"": "cp437", # dos latine us
863 #"": "cp850", # dos latin 1
864 #"": "cp852", # dos latin 2
867 #"iso-8859-6": "" # arabic
868 #"iso-8859-7": "" # greek
869 #"iso-8859-8": "" # hebrew
870 #"iso-8859-10": "" # latin6, more complete iso-8859-4
872 if docutils_encoding
.lower() in tr
:
873 return tr
[docutils_encoding
.lower()]
874 # convert: latin-1 and utf-8 and similar things
875 return docutils_encoding
.replace("_", "").replace("-", "").lower()
877 def language_label(self
, docutil_label
):
878 return self
.language
.labels
[docutil_label
]
880 latex_equivalents
= {
890 u
'\u2020' : '{\\dag}',
891 u
'\u2021' : '{\\ddag}',
892 u
'\u2026' : '{\\dots}',
893 u
'\u2122' : '{\\texttrademark}',
894 u
'\u21d4' : '{$\\Leftrightarrow$}',
898 def unicode_to_latex(self
,text
):
900 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252124
901 # Only some special chracters are translated, for documents with many
902 # utf-8 chars one should use the LaTeX unicode package.
903 for uchar
in self
.latex_equivalents
.keys():
904 text
= text
.replace(uchar
,self
.latex_equivalents
[uchar
])
907 def ensure_math(self
, text
):
908 if not 'ensure_math_re' in self
.__dict
__:
910 # lnot,pm,twosuperior,threesuperior,mu,onesuperior,times,div
911 'latin1' : '\xac\xb1\xb2\xb3\xb5\xb9\xd7\xf7' ,
912 # also latin5 and latin9
914 self
.ensure_math_re
= re
.compile('([%s])' % chars
['latin1'])
915 text
= self
.ensure_math_re
.sub(r
'\\ensuremath{\1}', text
)
918 def encode(self
, text
):
920 Encode special characters (``# $ % & ~ _ ^ \ { }``) in `text` & return
922 # Escaping with a backslash does not help with backslashes, ~ and ^.
924 # < > are only available in math-mode or tt font. (really ?)
925 # $ starts math- mode.
929 # compile the regexps once. do it here so one can see them.
932 if not 'encode_re_braces' in self
.__dict
__:
933 self
.encode_re_braces
= re
.compile(r
'([{}])')
934 text
= self
.encode_re_braces
.sub(r
'{\\\1}',text
)
935 if not 'encode_re_bslash' in self
.__dict
__:
936 # find backslash: except in the form '{\{}' or '{\}}'.
937 self
.encode_re_bslash
= re
.compile(r
'(?<!{)(\\)(?![{}]})')
938 # then the backslash: except in the form from line above:
939 # either '{\{}' or '{\}}'.
940 text
= self
.encode_re_bslash
.sub(r
'{\\textbackslash}', text
)
943 text
= text
.replace("$", '{\\$}')
944 if not ( self
.literal_block
or self
.literal
or self
.mathmode
):
945 # the vertical bar: in mathmode |,\vert or \mid
946 # in textmode \textbar
947 text
= text
.replace("|", '{\\textbar}')
948 text
= text
.replace("<", '{\\textless}')
949 text
= text
.replace(">", '{\\textgreater}')
951 text
= text
.replace("&", '{\\&}')
953 # * verb|^| does not work in mbox.
954 # * mathmode has wedge. hat{~} would also work.
955 # text = text.replace("^", '{\\ensuremath{^\\wedge}}')
956 text
= text
.replace("^", '{\\textasciicircum}')
957 text
= text
.replace("%", '{\\%}')
958 text
= text
.replace("#", '{\\#}')
959 text
= text
.replace("~", '{\\textasciitilde}')
960 # Separate compound characters, e.g. "--" to "-{}-". (The
961 # actual separation is done later; see below.)
963 if self
.literal_block
or self
.literal
:
964 # In monospace-font, we also separate ",,", "``" and "''"
965 # and some other characters which can't occur in
967 separate_chars
+= ',`\'"<>'
968 # pdflatex does not produce doublequotes for ngerman.
969 text
= self
.babel
.double_quotes_in_tt(text
)
970 if self
.font_encoding
== 'OT1':
971 # We're using OT1 font-encoding and have to replace
972 # underscore by underlined blank, because this has
974 text
= text
.replace('_', '{\\underline{ }}')
975 # And the tt-backslash doesn't work in OT1, so we use
977 text
= text
.replace('\\textbackslash', '\\reflectbox{/}')
979 text
= text
.replace('_', '{\\_}')
981 text
= self
.babel
.quote_quotes(text
)
982 if not self
.inside_citation_reference_label
:
983 text
= text
.replace("_", '{\\_}')
984 for char
in separate_chars
* 2:
985 # Do it twice ("* 2") becaues otherwise we would replace
987 text
= text
.replace(char
+ char
, char
+ '{}' + char
)
988 if self
.insert_newline
or self
.literal_block
:
989 # Insert a blank before the newline, to avoid
990 # ! LaTeX Error: There's no line here to end.
991 text
= text
.replace("\n", '~\\\\\n')
992 elif self
.mbox_newline
:
993 # TODO dead code: remove after 0.5 release
994 if self
.literal_block
:
995 closings
= "}" * len(self
.literal_block_stack
)
996 openings
= "".join(self
.literal_block_stack
)
1000 text
= text
.replace("\n", "%s}\\\\\n\\mbox{%s" % (closings
,openings
))
1001 text
= text
.replace('[', '{[}').replace(']', '{]}')
1002 if self
.insert_none_breaking_blanks
:
1003 text
= text
.replace(' ', self
.babel
.nobr
)
1004 if self
.latex_encoding
!= 'utf8':
1005 text
= self
.unicode_to_latex(text
)
1006 text
= self
.ensure_math(text
)
1009 def literal_block_env(self
, begin_or_end
):
1012 if self
.settings
.literal_block_env
!= '':
1013 (none
, env
, opt
, none
) = re
.split("(\w+)(.*)",
1014 self
.settings
.literal_block_env
)
1015 if begin_or_end
== 'begin':
1016 return '\\begin{%s}%s\n' % (env
, opt
)
1017 return '\n\\end{%s}\n' % (env
, )
1021 def attval(self
, text
,
1022 whitespace
=re
.compile('[\n\r\t\v\f]')):
1023 """Cleanse, encode, and return attribute value text."""
1024 return self
.encode(whitespace
.sub(' ', text
))
1027 if self
.pdfinfo
is not None and self
.pdfauthor
:
1028 self
.pdfinfo
.append('pdfauthor={%s}' % self
.pdfauthor
)
1030 pdfinfo
= '\\hypersetup{\n' + ',\n'.join(self
.pdfinfo
) + '\n}\n'
1033 head
= '\\title{%s}\n\\author{%s}\n\\date{%s}\n' % \
1035 ' \\and\n'.join(['~\\\\\n'.join(author_lines
)
1036 for author_lines
in self
.author_stack
]),
1038 return ''.join(self
.head_prefix
+ [head
] + self
.head
+ [pdfinfo
]
1039 + self
.body_prefix
+ self
.body
+ self
.body_suffix
)
1041 def visit_Text(self
, node
):
1042 self
.body
.append(self
.encode(node
.astext()))
1044 def depart_Text(self
, node
):
1047 def visit_address(self
, node
):
1048 self
.visit_docinfo_item(node
, 'address')
1050 def depart_address(self
, node
):
1051 self
.depart_docinfo_item(node
)
1053 def visit_admonition(self
, node
, name
=''):
1054 self
.body
.append('\\begin{center}\\begin{sffamily}\n')
1055 self
.body
.append('\\fbox{\\parbox{\\admonitionwidth}{\n')
1057 self
.body
.append('\\textbf{\\large '+ self
.language
.labels
[name
] + '}\n');
1058 self
.body
.append('\\vspace{2mm}\n')
1061 def depart_admonition(self
, node
=None):
1062 self
.body
.append('}}\n') # end parbox fbox
1063 self
.body
.append('\\end{sffamily}\n\\end{center}\n');
1065 def visit_attention(self
, node
):
1066 self
.visit_admonition(node
, 'attention')
1068 def depart_attention(self
, node
):
1069 self
.depart_admonition()
1071 def visit_author(self
, node
):
1072 self
.visit_docinfo_item(node
, 'author')
1074 def depart_author(self
, node
):
1075 self
.depart_docinfo_item(node
)
1077 def visit_authors(self
, node
):
1078 # not used: visit_author is called anyway for each author.
1081 def depart_authors(self
, node
):
1084 def visit_block_quote(self
, node
):
1085 self
.body
.append( '\\begin{quote}\n')
1087 def depart_block_quote(self
, node
):
1088 self
.body
.append( '\\end{quote}\n')
1090 def visit_bullet_list(self
, node
):
1091 if 'contents' in self
.topic_classes
:
1092 if self
.use_latex_toc
:
1093 raise nodes
.SkipNode
1094 self
.body
.append( '\\begin{list}{}{}\n' )
1096 self
.body
.append( '\\begin{itemize}\n' )
1098 def depart_bullet_list(self
, node
):
1099 if 'contents' in self
.topic_classes
:
1100 self
.body
.append( '\\end{list}\n' )
1102 self
.body
.append( '\\end{itemize}\n' )
1104 # Imperfect superscript/subscript handling: mathmode italicizes
1105 # all letters by default.
1106 def visit_superscript(self
, node
):
1107 self
.body
.append('$^{')
1110 def depart_superscript(self
, node
):
1111 self
.body
.append('}$')
1114 def visit_subscript(self
, node
):
1115 self
.body
.append('$_{')
1118 def depart_subscript(self
, node
):
1119 self
.body
.append('}$')
1122 def visit_caption(self
, node
):
1123 self
.body
.append( '\\caption{' )
1125 def depart_caption(self
, node
):
1126 self
.body
.append('}')
1128 def visit_caution(self
, node
):
1129 self
.visit_admonition(node
, 'caution')
1131 def depart_caution(self
, node
):
1132 self
.depart_admonition()
1134 def visit_title_reference(self
, node
):
1135 self
.body
.append( '\\titlereference{' )
1137 def depart_title_reference(self
, node
):
1138 self
.body
.append( '}' )
1140 def visit_citation(self
, node
):
1141 # TODO maybe use cite bibitems
1142 if self
._use
_latex
_citations
:
1143 self
.context
.append(len(self
.body
))
1145 self
.body
.append('\\begin{figure}[b]')
1146 for id in node
['ids']:
1147 self
.body
.append('\\hypertarget{%s}' % id)
1149 def depart_citation(self
, node
):
1150 if self
._use
_latex
_citations
:
1151 size
= self
.context
.pop()
1152 label
= self
.body
[size
]
1153 text
= ''.join(self
.body
[size
+1:])
1154 del self
.body
[size
:]
1155 self
._bibitems
.append([label
, text
])
1157 self
.body
.append('\\end{figure}\n')
1159 def visit_citation_reference(self
, node
):
1160 if self
._use
_latex
_citations
:
1161 if not self
.inside_citation_reference_label
:
1162 self
.body
.append('\\cite{')
1163 self
.inside_citation_reference_label
= 1
1165 assert self
.body
[-1] in (' ', '\n'),\
1166 'unexpected non-whitespace while in reference label'
1171 href
= node
['refid']
1172 elif 'refname' in node
:
1173 href
= self
.document
.nameids
[node
['refname']]
1174 self
.body
.append('[\\hyperlink{%s}{' % href
)
1176 def depart_citation_reference(self
, node
):
1177 if self
._use
_latex
_citations
:
1178 followup_citation
= False
1179 # check for a following citation separated by a space or newline
1180 next_siblings
= node
.traverse(descend
=0, siblings
=1, include_self
=0)
1181 if len(next_siblings
) > 1:
1182 next
= next_siblings
[0]
1183 if (isinstance(next
, nodes
.Text
)
1184 and next
.astext() in (' ', '\n')):
1185 if next_siblings
[1].__class
__ == node
.__class
__:
1186 followup_citation
= True
1187 if followup_citation
:
1188 self
.body
.append(',')
1190 self
.body
.append('}')
1191 self
.inside_citation_reference_label
= 0
1193 self
.body
.append('}]')
1195 def visit_classifier(self
, node
):
1196 self
.body
.append( '(\\textbf{' )
1198 def depart_classifier(self
, node
):
1199 self
.body
.append( '})\n' )
1201 def visit_colspec(self
, node
):
1202 self
.active_table
.visit_colspec(node
)
1204 def depart_colspec(self
, node
):
1207 def visit_comment(self
, node
):
1208 # Escape end of line by a new comment start in comment text.
1209 self
.body
.append('%% %s \n' % node
.astext().replace('\n', '\n% '))
1210 raise nodes
.SkipNode
1212 def visit_compound(self
, node
):
1215 def depart_compound(self
, node
):
1218 def visit_contact(self
, node
):
1219 self
.visit_docinfo_item(node
, 'contact')
1221 def depart_contact(self
, node
):
1222 self
.depart_docinfo_item(node
)
1224 def visit_container(self
, node
):
1227 def depart_container(self
, node
):
1230 def visit_copyright(self
, node
):
1231 self
.visit_docinfo_item(node
, 'copyright')
1233 def depart_copyright(self
, node
):
1234 self
.depart_docinfo_item(node
)
1236 def visit_danger(self
, node
):
1237 self
.visit_admonition(node
, 'danger')
1239 def depart_danger(self
, node
):
1240 self
.depart_admonition()
1242 def visit_date(self
, node
):
1243 self
.visit_docinfo_item(node
, 'date')
1245 def depart_date(self
, node
):
1246 self
.depart_docinfo_item(node
)
1248 def visit_decoration(self
, node
):
1251 def depart_decoration(self
, node
):
1254 def visit_definition(self
, node
):
1257 def depart_definition(self
, node
):
1258 self
.body
.append('\n')
1260 def visit_definition_list(self
, node
):
1261 self
.body
.append( '\\begin{description}\n' )
1263 def depart_definition_list(self
, node
):
1264 self
.body
.append( '\\end{description}\n' )
1266 def visit_definition_list_item(self
, node
):
1269 def depart_definition_list_item(self
, node
):
1272 def visit_description(self
, node
):
1273 self
.body
.append( ' ' )
1275 def depart_description(self
, node
):
1278 def visit_docinfo(self
, node
):
1280 self
.docinfo
.append('%' + '_'*75 + '\n')
1281 self
.docinfo
.append('\\begin{center}\n')
1282 self
.docinfo
.append('\\begin{tabularx}{\\docinfowidth}{lX}\n')
1284 def depart_docinfo(self
, node
):
1285 self
.docinfo
.append('\\end{tabularx}\n')
1286 self
.docinfo
.append('\\end{center}\n')
1287 self
.body
= self
.docinfo
+ self
.body
1288 # clear docinfo, so field names are no longer appended.
1291 def visit_docinfo_item(self
, node
, name
):
1292 if name
== 'author':
1293 if not self
.pdfinfo
== None:
1294 if not self
.pdfauthor
:
1295 self
.pdfauthor
= self
.attval(node
.astext())
1297 self
.pdfauthor
+= self
.author_separator
+ self
.attval(node
.astext())
1298 if self
.use_latex_docinfo
:
1299 if name
in ('author', 'organization', 'contact', 'address'):
1300 # We attach these to the last author. If any of them precedes
1301 # the first author, put them in a separate "author" group (for
1302 # no better semantics).
1303 if name
== 'author' or not self
.author_stack
:
1304 self
.author_stack
.append([])
1305 if name
== 'address': # newlines are meaningful
1306 self
.insert_newline
= 1
1307 text
= self
.encode(node
.astext())
1308 self
.insert_newline
= 0
1310 text
= self
.attval(node
.astext())
1311 self
.author_stack
[-1].append(text
)
1312 raise nodes
.SkipNode
1313 elif name
== 'date':
1314 self
.date
= self
.attval(node
.astext())
1315 raise nodes
.SkipNode
1316 self
.docinfo
.append('\\textbf{%s}: &\n\t' % self
.language_label(name
))
1317 if name
== 'address':
1318 self
.insert_newline
= 1
1319 self
.docinfo
.append('{\\raggedright\n')
1320 self
.context
.append(' } \\\\\n')
1322 self
.context
.append(' \\\\\n')
1323 self
.context
.append(self
.docinfo
)
1324 self
.context
.append(len(self
.body
))
1326 def depart_docinfo_item(self
, node
):
1327 size
= self
.context
.pop()
1328 dest
= self
.context
.pop()
1329 tail
= self
.context
.pop()
1330 tail
= self
.body
[size
:] + [tail
]
1331 del self
.body
[size
:]
1333 # for address we did set insert_newline
1334 self
.insert_newline
= 0
1336 def visit_doctest_block(self
, node
):
1337 self
.body
.append( '\\begin{verbatim}' )
1340 def depart_doctest_block(self
, node
):
1341 self
.body
.append( '\\end{verbatim}\n' )
1344 def visit_document(self
, node
):
1345 self
.body_prefix
.append('\\begin{document}\n')
1347 if self
.use_latex_docinfo
or len(node
) and isinstance(node
[0], nodes
.title
):
1348 self
.body_prefix
.append('\\maketitle\n')
1349 # alternative use titlepage environment.
1352 self
.body
.append('\n\\setlength{\\locallinewidth}{\\linewidth}\n')
1354 def depart_document(self
, node
):
1355 # TODO insertion point of bibliography should none automatic.
1356 if self
._use
_latex
_citations
and len(self
._bibitems
)>0:
1359 for bi
in self
._bibitems
:
1360 if len(widest_label
)<len(bi
[0]):
1361 widest_label
= bi
[0]
1362 self
.body
.append('\n\\begin{thebibliography}{%s}\n'%widest_label
)
1363 for bi
in self
._bibitems
:
1364 # cite_key: underscores must not be escaped
1365 cite_key
= bi
[0].replace(r
"{\_}","_")
1366 self
.body
.append('\\bibitem[%s]{%s}{%s}\n' % (bi
[0], cite_key
, bi
[1]))
1367 self
.body
.append('\\end{thebibliography}\n')
1369 self
.body
.append('\n\\bibliographystyle{%s}\n' % self
.bibtex
[0])
1370 self
.body
.append('\\bibliography{%s}\n' % self
.bibtex
[1])
1372 self
.body_suffix
.append('\\end{document}\n')
1374 def visit_emphasis(self
, node
):
1375 self
.body
.append('\\emph{')
1376 self
.literal_block_stack
.append('\\emph{')
1378 def depart_emphasis(self
, node
):
1379 self
.body
.append('}')
1380 self
.literal_block_stack
.pop()
1382 def visit_entry(self
, node
):
1383 self
.active_table
.visit_entry()
1385 if self
.active_table
.get_entry_number() == 1:
1386 # if the firstrow is a multirow, this actually is the second row.
1387 # this gets hairy if rowspans follow each other.
1388 if self
.active_table
.get_rowspan(0):
1390 while self
.active_table
.get_rowspan(count
):
1392 self
.body
.append(' & ')
1393 self
.active_table
.visit_entry() # increment cell count
1395 self
.body
.append(' & ')
1398 # IN WORK BUG TODO HACK continues here
1399 # multirow in LaTeX simply will enlarge the cell over several rows
1400 # (the following n if n is positive, the former if negative).
1401 if 'morerows' in node
and 'morecols' in node
:
1402 raise NotImplementedError('Cells that '
1403 'span multiple rows *and* columns are not supported, sorry.')
1404 if 'morerows' in node
:
1405 count
= node
['morerows'] + 1
1406 self
.active_table
.set_rowspan(self
.active_table
.get_entry_number()-1,count
)
1407 self
.body
.append('\\multirow{%d}{%s}{' % \
1408 (count
,self
.active_table
.get_column_width()))
1409 self
.context
.append('}')
1410 # BUG following rows must have empty cells.
1411 elif 'morecols' in node
:
1412 # the vertical bar before column is missing if it is the first column.
1413 # the one after always.
1414 if self
.active_table
.get_entry_number() == 1:
1415 bar1
= self
.active_table
.get_vertical_bar()
1418 count
= node
['morecols'] + 1
1419 self
.body
.append('\\multicolumn{%d}{%sl%s}{' % \
1420 (count
, bar1
, self
.active_table
.get_vertical_bar()))
1421 self
.context
.append('}')
1423 self
.context
.append('')
1425 # header / not header
1426 if isinstance(node
.parent
.parent
, nodes
.thead
):
1427 self
.body
.append('\\textbf{')
1428 self
.context
.append('}')
1429 elif self
.active_table
.is_stub_column():
1430 self
.body
.append('\\textbf{')
1431 self
.context
.append('}')
1433 self
.context
.append('')
1435 def depart_entry(self
, node
):
1436 self
.body
.append(self
.context
.pop()) # header / not header
1437 self
.body
.append(self
.context
.pop()) # multirow/column
1438 # if following row is spanned from above.
1439 if self
.active_table
.get_rowspan(self
.active_table
.get_entry_number()):
1440 self
.body
.append(' & ')
1441 self
.active_table
.visit_entry() # increment cell count
1443 def visit_row(self
, node
):
1444 self
.active_table
.visit_row()
1446 def depart_row(self
, node
):
1447 self
.body
.extend(self
.active_table
.depart_row())
1449 def visit_enumerated_list(self
, node
):
1450 # We create our own enumeration list environment.
1451 # This allows to set the style and starting value
1452 # and unlimited nesting.
1453 enum_style
= {'arabic':'arabic',
1454 'loweralpha':'alph',
1455 'upperalpha':'Alph',
1456 'lowerroman':'roman',
1457 'upperroman':'Roman' }
1459 if 'suffix' in node
:
1460 enum_suffix
= node
['suffix']
1462 if 'prefix' in node
:
1463 enum_prefix
= node
['prefix']
1464 if self
.compound_enumerators
:
1466 if self
.section_prefix_for_enumerators
and self
.section_level
:
1467 for i
in range(self
.section_level
):
1468 pref
+= '%d.' % self
._section
_number
[i
]
1469 pref
= pref
[:-1] + self
.section_enumerator_separator
1471 for ctype
, cname
in self
._enumeration
_counters
:
1472 enum_prefix
+= '\\%s{%s}.' % (ctype
, cname
)
1473 enum_type
= "arabic"
1474 if 'enumtype' in node
:
1475 enum_type
= node
['enumtype']
1476 if enum_type
in enum_style
:
1477 enum_type
= enum_style
[enum_type
]
1479 counter_name
= "listcnt%d" % len(self
._enumeration
_counters
)
1480 self
._enumeration
_counters
.append((enum_type
, counter_name
))
1481 # If we haven't used this counter name before, then create a
1482 # new counter; otherwise, reset & reuse the old counter.
1483 if len(self
._enumeration
_counters
) > self
._max
_enumeration
_counters
:
1484 self
._max
_enumeration
_counters
= len(self
._enumeration
_counters
)
1485 self
.body
.append('\\newcounter{%s}\n' % counter_name
)
1487 self
.body
.append('\\setcounter{%s}{0}\n' % counter_name
)
1489 self
.body
.append('\\begin{list}{%s\\%s{%s}%s}\n' % \
1490 (enum_prefix
,enum_type
,counter_name
,enum_suffix
))
1491 self
.body
.append('{\n')
1492 self
.body
.append('\\usecounter{%s}\n' % counter_name
)
1493 # set start after usecounter, because it initializes to zero.
1495 self
.body
.append('\\addtocounter{%s}{%d}\n' \
1496 % (counter_name
,node
['start']-1))
1497 ## set rightmargin equal to leftmargin
1498 self
.body
.append('\\setlength{\\rightmargin}{\\leftmargin}\n')
1499 self
.body
.append('}\n')
1501 def depart_enumerated_list(self
, node
):
1502 self
.body
.append('\\end{list}\n')
1503 self
._enumeration
_counters
.pop()
1505 def visit_error(self
, node
):
1506 self
.visit_admonition(node
, 'error')
1508 def depart_error(self
, node
):
1509 self
.depart_admonition()
1511 def visit_field(self
, node
):
1512 # real output is done in siblings: _argument, _body, _name
1515 def depart_field(self
, node
):
1516 self
.body
.append('\n')
1517 ##self.body.append('%[depart_field]\n')
1519 def visit_field_argument(self
, node
):
1520 self
.body
.append('%[visit_field_argument]\n')
1522 def depart_field_argument(self
, node
):
1523 self
.body
.append('%[depart_field_argument]\n')
1525 def visit_field_body(self
, node
):
1526 # BUG by attach as text we loose references.
1528 self
.docinfo
.append('%s \\\\\n' % self
.encode(node
.astext()))
1529 raise nodes
.SkipNode
1530 # BUG: what happens if not docinfo
1532 def depart_field_body(self
, node
):
1533 self
.body
.append( '\n' )
1535 def visit_field_list(self
, node
):
1536 if not self
.docinfo
:
1537 self
.body
.append('\\begin{quote}\n')
1538 self
.body
.append('\\begin{description}\n')
1540 def depart_field_list(self
, node
):
1541 if not self
.docinfo
:
1542 self
.body
.append('\\end{description}\n')
1543 self
.body
.append('\\end{quote}\n')
1545 def visit_field_name(self
, node
):
1546 # BUG this duplicates docinfo_item
1548 self
.docinfo
.append('\\textbf{%s}: &\n\t' % self
.encode(node
.astext()))
1549 raise nodes
.SkipNode
1551 self
.body
.append('\\item [')
1553 def depart_field_name(self
, node
):
1554 if not self
.docinfo
:
1555 self
.body
.append(':]')
1557 def visit_figure(self
, node
):
1558 if ('align' not in node
.attributes
or
1559 node
.attributes
['align'] == 'center'):
1560 # centering does not add vertical space like center.
1561 align
= '\n\\centering'
1564 # TODO non vertical space for other alignments.
1565 align
= '\\begin{flush%s}' % node
.attributes
['align']
1566 align_end
= '\\end{flush%s}' % node
.attributes
['align']
1567 self
.body
.append( '\\begin{figure}[htbp]%s\n' % align
)
1568 self
.context
.append( '%s\\end{figure}\n' % align_end
)
1570 def depart_figure(self
, node
):
1571 self
.body
.append( self
.context
.pop() )
1573 def visit_footer(self
, node
):
1574 self
.context
.append(len(self
.body
))
1576 def depart_footer(self
, node
):
1577 start
= self
.context
.pop()
1578 footer
= (['\n\\begin{center}\small\n']
1579 + self
.body
[start
:] + ['\n\\end{center}\n'])
1580 self
.body_suffix
[:0] = footer
1581 del self
.body
[start
:]
1583 def visit_footnote(self
, node
):
1584 if self
.use_latex_footnotes
:
1585 num
,text
= node
.astext().split(None,1)
1586 num
= self
.encode(num
.strip())
1587 self
.body
.append('\\footnotetext['+num
+']')
1588 self
.body
.append('{')
1590 self
.body
.append('\\begin{figure}[b]')
1591 for id in node
['ids']:
1592 self
.body
.append('\\hypertarget{%s}' % id)
1594 def depart_footnote(self
, node
):
1595 if self
.use_latex_footnotes
:
1596 self
.body
.append('}\n')
1598 self
.body
.append('\\end{figure}\n')
1600 def visit_footnote_reference(self
, node
):
1601 if self
.use_latex_footnotes
:
1602 self
.body
.append("\\footnotemark["+self
.encode(node
.astext())+"]")
1603 raise nodes
.SkipNode
1606 href
= node
['refid']
1607 elif 'refname' in node
:
1608 href
= self
.document
.nameids
[node
['refname']]
1609 format
= self
.settings
.footnote_references
1610 if format
== 'brackets':
1612 self
.context
.append(']')
1613 elif format
== 'superscript':
1614 suffix
= '\\raisebox{.5em}[0em]{\\scriptsize'
1615 self
.context
.append('}')
1616 else: # shouldn't happen
1617 raise AssertionError('Illegal footnote reference format.')
1618 self
.body
.append('%s\\hyperlink{%s}{' % (suffix
,href
))
1620 def depart_footnote_reference(self
, node
):
1621 if self
.use_latex_footnotes
:
1623 self
.body
.append('}%s' % self
.context
.pop())
1625 # footnote/citation label
1626 def label_delim(self
, node
, bracket
, superscript
):
1627 if isinstance(node
.parent
, nodes
.footnote
):
1628 if self
.use_latex_footnotes
:
1629 raise nodes
.SkipNode
1630 if self
.settings
.footnote_references
== 'brackets':
1631 self
.body
.append(bracket
)
1633 self
.body
.append(superscript
)
1635 assert isinstance(node
.parent
, nodes
.citation
)
1636 if not self
._use
_latex
_citations
:
1637 self
.body
.append(bracket
)
1639 def visit_label(self
, node
):
1640 self
.label_delim(node
, '[', '$^{')
1642 def depart_label(self
, node
):
1643 self
.label_delim(node
, ']', '}$')
1645 # elements generated by the framework e.g. section numbers.
1646 def visit_generated(self
, node
):
1649 def depart_generated(self
, node
):
1652 def visit_header(self
, node
):
1653 self
.context
.append(len(self
.body
))
1655 def depart_header(self
, node
):
1656 start
= self
.context
.pop()
1657 self
.body_prefix
.append('\n\\verb|begin_header|\n')
1658 self
.body_prefix
.extend(self
.body
[start
:])
1659 self
.body_prefix
.append('\n\\verb|end_header|\n')
1660 del self
.body
[start
:]
1662 def visit_hint(self
, node
):
1663 self
.visit_admonition(node
, 'hint')
1665 def depart_hint(self
, node
):
1666 self
.depart_admonition()
1668 def latex_image_length(self
, width_str
):
1669 match
= re
.match('(\d*\.?\d*)\s*(\S*)', width_str
)
1674 amount
, unit
= match
.groups()[:2]
1676 # LaTeX does not know pixels but points
1677 res
= "%spt" % amount
1679 res
= "%.3f\\linewidth" % (float(amount
)/100.0)
1682 def visit_image(self
, node
):
1683 attrs
= node
.attributes
1684 # Add image URI to dependency list, assuming that it's
1685 # referring to a local file.
1686 self
.settings
.record_dependencies
.add(attrs
['uri'])
1687 pre
= [] # in reverse order
1689 include_graphics_options
= []
1690 inline
= isinstance(node
.parent
, nodes
.TextElement
)
1691 if 'scale' in attrs
:
1692 # Could also be done with ``scale`` option to
1693 # ``\includegraphics``; doing it this way for consistency.
1694 pre
.append('\\scalebox{%f}{' % (attrs
['scale'] / 100.0,))
1696 if 'width' in attrs
:
1697 include_graphics_options
.append('width=%s' % (
1698 self
.latex_image_length(attrs
['width']), ))
1699 if 'height' in attrs
:
1700 include_graphics_options
.append('height=%s' % (
1701 self
.latex_image_length(attrs
['height']), ))
1702 if 'align' in attrs
:
1704 # By default latex aligns the bottom of an image.
1705 (1, 'bottom'): ('', ''),
1706 (1, 'middle'): ('\\raisebox{-0.5\\height}{', '}'),
1707 (1, 'top'): ('\\raisebox{-\\height}{', '}'),
1708 (0, 'center'): ('{\\hfill', '\\hfill}'),
1709 # These 2 don't exactly do the right thing. The image should
1710 # be floated alongside the paragraph. See
1711 # http://www.w3.org/TR/html4/struct/objects.html#adef-align-IMG
1712 (0, 'left'): ('{', '\\hfill}'),
1713 (0, 'right'): ('{\\hfill', '}'),}
1715 pre
.append(align_prepost
[inline
, attrs
['align']][0])
1716 post
.append(align_prepost
[inline
, attrs
['align']][1])
1718 pass # XXX complain here?
1723 self
.body
.extend( pre
)
1725 if len(include_graphics_options
)>0:
1726 options
= '[%s]' % (','.join(include_graphics_options
))
1727 self
.body
.append( '\\includegraphics%s{%s}' % (
1728 options
, attrs
['uri'] ) )
1729 self
.body
.extend( post
)
1731 def depart_image(self
, node
):
1734 def visit_important(self
, node
):
1735 self
.visit_admonition(node
, 'important')
1737 def depart_important(self
, node
):
1738 self
.depart_admonition()
1740 def visit_interpreted(self
, node
):
1741 # @@@ Incomplete, pending a proper implementation on the
1742 # Parser/Reader end.
1743 self
.visit_literal(node
)
1745 def depart_interpreted(self
, node
):
1746 self
.depart_literal(node
)
1748 def visit_legend(self
, node
):
1749 self
.body
.append('{\\small ')
1751 def depart_legend(self
, node
):
1752 self
.body
.append('}')
1754 def visit_line(self
, node
):
1755 self
.body
.append('\item[] ')
1757 def depart_line(self
, node
):
1758 self
.body
.append('\n')
1760 def visit_line_block(self
, node
):
1761 if isinstance(node
.parent
, nodes
.line_block
):
1762 self
.body
.append('\\item[] \n'
1763 '\\begin{lineblock}{\\lineblockindentation}\n')
1765 self
.body
.append('\n\\begin{lineblock}{0em}\n')
1767 def depart_line_block(self
, node
):
1768 self
.body
.append('\\end{lineblock}\n')
1770 def visit_list_item(self
, node
):
1771 # Append "{}" in case the next character is "[", which would break
1772 # LaTeX's list environment (no numbering and the "[" is not printed).
1773 self
.body
.append('\\item {} ')
1775 def depart_list_item(self
, node
):
1776 self
.body
.append('\n')
1778 def visit_literal(self
, node
):
1780 self
.body
.append('\\texttt{')
1782 def depart_literal(self
, node
):
1783 self
.body
.append('}')
1786 def visit_literal_block(self
, node
):
1788 Render a literal-block.
1790 Literal blocks are used for "::"-prefixed literal-indented
1791 blocks of text, where the inline markup is not recognized,
1792 but are also the product of the parsed-literal directive,
1793 where the markup is respected.
1795 # In both cases, we want to use a typewriter/monospaced typeface.
1796 # For "real" literal-blocks, we can use \verbatim, while for all
1797 # the others we must use \mbox.
1799 # We can distinguish between the two kinds by the number of
1800 # siblings that compose this node: if it is composed by a
1801 # single element, it's surely either a real one or a
1802 # parsed-literal that does not contain any markup.
1804 if not self
.active_table
.is_open():
1805 # no quote inside tables, to avoid vertical space between
1806 # table border and literal block.
1807 # BUG: fails if normal text preceeds the literal block.
1808 self
.body
.append('\\begin{quote}')
1809 self
.context
.append('\\end{quote}\n')
1811 self
.body
.append('\n')
1812 self
.context
.append('\n')
1813 if (self
.settings
.use_verbatim_when_possible
and (len(node
) == 1)
1814 # in case of a parsed-literal containing just a "**bold**" word:
1815 and isinstance(node
[0], nodes
.Text
)):
1817 self
.body
.append(self
.literal_block_env('begin'))
1819 self
.literal_block
= 1
1820 self
.insert_none_breaking_blanks
= 1
1821 self
.body
.append('{\\ttfamily \\raggedright \\noindent\n')
1822 # * obey..: is from julien and never worked for me (grubert).
1823 # self.body.append('{\\obeylines\\obeyspaces\\ttfamily\n')
1825 def depart_literal_block(self
, node
):
1827 self
.body
.append(self
.literal_block_env('end'))
1830 self
.body
.append('\n}')
1831 self
.insert_none_breaking_blanks
= 0
1832 self
.literal_block
= 0
1833 # obey end: self.body.append('}\n')
1834 self
.body
.append(self
.context
.pop())
1836 def visit_meta(self
, node
):
1837 self
.body
.append('[visit_meta]\n')
1838 # BUG maybe set keywords for pdf
1839 ##self.head.append(self.starttag(node, 'meta', **node.attributes))
1841 def depart_meta(self
, node
):
1842 self
.body
.append('[depart_meta]\n')
1844 def visit_note(self
, node
):
1845 self
.visit_admonition(node
, 'note')
1847 def depart_note(self
, node
):
1848 self
.depart_admonition()
1850 def visit_option(self
, node
):
1851 if self
.context
[-1]:
1852 # this is not the first option
1853 self
.body
.append(', ')
1855 def depart_option(self
, node
):
1856 # flag tha the first option is done.
1857 self
.context
[-1] += 1
1859 def visit_option_argument(self
, node
):
1860 """The delimiter betweeen an option and its argument."""
1861 self
.body
.append(node
.get('delimiter', ' '))
1863 def depart_option_argument(self
, node
):
1866 def visit_option_group(self
, node
):
1867 self
.body
.append('\\item [')
1868 # flag for first option
1869 self
.context
.append(0)
1871 def depart_option_group(self
, node
):
1872 self
.context
.pop() # the flag
1873 self
.body
.append('] ')
1875 def visit_option_list(self
, node
):
1876 self
.body
.append('\\begin{optionlist}{3cm}\n')
1878 def depart_option_list(self
, node
):
1879 self
.body
.append('\\end{optionlist}\n')
1881 def visit_option_list_item(self
, node
):
1884 def depart_option_list_item(self
, node
):
1887 def visit_option_string(self
, node
):
1888 ##self.body.append(self.starttag(node, 'span', '', CLASS='option'))
1891 def depart_option_string(self
, node
):
1892 ##self.body.append('</span>')
1895 def visit_organization(self
, node
):
1896 self
.visit_docinfo_item(node
, 'organization')
1898 def depart_organization(self
, node
):
1899 self
.depart_docinfo_item(node
)
1901 def visit_paragraph(self
, node
):
1902 index
= node
.parent
.index(node
)
1903 if not ('contents' in self
.topic_classes
or
1904 (isinstance(node
.parent
, nodes
.compound
) and
1906 not isinstance(node
.parent
[index
- 1], nodes
.paragraph
) and
1907 not isinstance(node
.parent
[index
- 1], nodes
.compound
))):
1908 self
.body
.append('\n')
1910 def depart_paragraph(self
, node
):
1911 self
.body
.append('\n')
1913 def visit_problematic(self
, node
):
1914 self
.body
.append('{\\color{red}\\bfseries{}')
1916 def depart_problematic(self
, node
):
1917 self
.body
.append('}')
1919 def visit_raw(self
, node
):
1920 if 'latex' in node
.get('format', '').split():
1921 self
.body
.append(node
.astext())
1922 raise nodes
.SkipNode
1924 def visit_reference(self
, node
):
1925 # BUG: hash_char "#" is trouble some in LaTeX.
1926 # mbox and other environment do not like the '#'.
1928 if 'refuri' in node
:
1929 href
= node
['refuri'].replace('#',hash_char
)
1930 elif 'refid' in node
:
1931 href
= hash_char
+ node
['refid']
1932 elif 'refname' in node
:
1933 href
= hash_char
+ self
.document
.nameids
[node
['refname']]
1935 raise AssertionError('Unknown reference.')
1936 self
.body
.append('\\href{%s}{' % href
.replace("%", "\\%"))
1937 if self
._reference
_label
and 'refuri' not in node
:
1938 self
.body
.append('\\%s{%s}}' % (self
._reference
_label
,
1939 href
.replace(hash_char
, '')))
1940 raise nodes
.SkipNode
1942 def depart_reference(self
, node
):
1943 self
.body
.append('}')
1945 def visit_revision(self
, node
):
1946 self
.visit_docinfo_item(node
, 'revision')
1948 def depart_revision(self
, node
):
1949 self
.depart_docinfo_item(node
)
1951 def visit_section(self
, node
):
1952 self
.section_level
+= 1
1953 # Initialize counter for potential subsections:
1954 self
._section
_number
.append(0)
1955 # Counter for this section's level (initialized by parent section):
1956 self
._section
_number
[self
.section_level
- 1] += 1
1958 def depart_section(self
, node
):
1959 # Remove counter for potential subsections:
1960 self
._section
_number
.pop()
1961 self
.section_level
-= 1
1963 def visit_sidebar(self
, node
):
1964 # BUG: this is just a hack to make sidebars render something
1965 self
.body
.append('\n\\setlength{\\locallinewidth}{0.9\\admonitionwidth}\n')
1966 self
.body
.append('\\begin{center}\\begin{sffamily}\n')
1967 self
.body
.append('\\fbox{\\colorbox[gray]{0.80}{\\parbox{\\admonitionwidth}{\n')
1969 def depart_sidebar(self
, node
):
1970 self
.body
.append('}}}\n') # end parbox colorbox fbox
1971 self
.body
.append('\\end{sffamily}\n\\end{center}\n');
1972 self
.body
.append('\n\\setlength{\\locallinewidth}{\\linewidth}\n')
1975 attribution_formats
= {'dash': ('---', ''),
1976 'parentheses': ('(', ')'),
1977 'parens': ('(', ')'),
1980 def visit_attribution(self
, node
):
1981 prefix
, suffix
= self
.attribution_formats
[self
.settings
.attribution
]
1982 self
.body
.append('\n\\begin{flushright}\n')
1983 self
.body
.append(prefix
)
1984 self
.context
.append(suffix
)
1986 def depart_attribution(self
, node
):
1987 self
.body
.append(self
.context
.pop() + '\n')
1988 self
.body
.append('\\end{flushright}\n')
1990 def visit_status(self
, node
):
1991 self
.visit_docinfo_item(node
, 'status')
1993 def depart_status(self
, node
):
1994 self
.depart_docinfo_item(node
)
1996 def visit_strong(self
, node
):
1997 self
.body
.append('\\textbf{')
1998 self
.literal_block_stack
.append('\\textbf{')
2000 def depart_strong(self
, node
):
2001 self
.body
.append('}')
2002 self
.literal_block_stack
.pop()
2004 def visit_substitution_definition(self
, node
):
2005 raise nodes
.SkipNode
2007 def visit_substitution_reference(self
, node
):
2008 self
.unimplemented_visit(node
)
2010 def visit_subtitle(self
, node
):
2011 if isinstance(node
.parent
, nodes
.sidebar
):
2012 self
.body
.append('~\\\\\n\\textbf{')
2013 self
.context
.append('}\n\\smallskip\n')
2014 elif isinstance(node
.parent
, nodes
.document
):
2015 self
.title
= self
.title
+ \
2016 '\\\\\n\\large{%s}\n' % self
.encode(node
.astext())
2017 raise nodes
.SkipNode
2018 elif isinstance(node
.parent
, nodes
.section
):
2019 self
.body
.append('\\textbf{')
2020 self
.context
.append('}\\vspace{0.2cm}\n\n\\noindent ')
2022 def depart_subtitle(self
, node
):
2023 self
.body
.append(self
.context
.pop())
2025 def visit_system_message(self
, node
):
2028 def depart_system_message(self
, node
):
2029 self
.body
.append('\n')
2031 def visit_table(self
, node
):
2032 if self
.active_table
.is_open():
2033 self
.table_stack
.append(self
.active_table
)
2034 # nesting longtable does not work (e.g. 2007-04-18)
2035 self
.active_table
= Table(self
,'tabular',self
.settings
.table_style
)
2036 self
.active_table
.open()
2037 for cl
in node
['classes']:
2038 self
.active_table
.set_table_style(cl
)
2039 self
.body
.append('\n' + self
.active_table
.get_opening())
2041 def depart_table(self
, node
):
2042 self
.body
.append(self
.active_table
.get_closing() + '\n')
2043 self
.active_table
.close()
2044 if len(self
.table_stack
)>0:
2045 self
.active_table
= self
.table_stack
.pop()
2047 self
.active_table
.set_table_style(self
.settings
.table_style
)
2049 def visit_target(self
, node
):
2050 # BUG: why not (refuri or refid or refname) means not footnote ?
2051 if not ('refuri' in node
or 'refid' in node
2052 or 'refname' in node
):
2053 for id in node
['ids']:
2054 self
.body
.append('\\hypertarget{%s}{' % id)
2055 self
.context
.append('}' * len(node
['ids']))
2056 elif node
.get("refid"):
2057 self
.body
.append('\\hypertarget{%s}{' % node
.get("refid"))
2058 self
.context
.append('}')
2060 self
.context
.append('')
2062 def depart_target(self
, node
):
2063 self
.body
.append(self
.context
.pop())
2065 def visit_tbody(self
, node
):
2066 # BUG write preamble if not yet done (colspecs not [])
2067 # for tables without heads.
2068 if not self
.active_table
.get('preamble written'):
2069 self
.visit_thead(None)
2070 self
.depart_thead(None)
2072 def depart_tbody(self
, node
):
2075 def visit_term(self
, node
):
2076 self
.body
.append('\\item[{')
2078 def depart_term(self
, node
):
2079 # definition list term.
2080 # \leavevmode results in a line break if the term is followed by a item list.
2081 self
.body
.append('}] \leavevmode ')
2083 def visit_tgroup(self
, node
):
2084 #self.body.append(self.starttag(node, 'colgroup'))
2085 #self.context.append('</colgroup>\n')
2088 def depart_tgroup(self
, node
):
2092 def thead_depth (self
):
2093 return self
._thead
_depth
2095 def visit_thead(self
, node
):
2096 self
._thead
_depth
+= 1
2097 if 1 == self
.thead_depth():
2098 self
.body
.append('{%s}\n' % self
.active_table
.get_colspecs())
2099 self
.active_table
.set('preamble written',1)
2100 self
.body
.append(self
.active_table
.get_caption())
2101 self
.body
.extend(self
.active_table
.visit_thead())
2103 def depart_thead(self
, node
):
2104 if node
is not None:
2105 self
.body
.extend(self
.active_table
.depart_thead())
2106 if self
.active_table
.need_recurse():
2107 node
.walkabout(self
)
2108 self
._thead
_depth
-= 1
2110 def visit_tip(self
, node
):
2111 self
.visit_admonition(node
, 'tip')
2113 def depart_tip(self
, node
):
2114 self
.depart_admonition()
2116 def bookmark(self
, node
):
2117 """Append latex href and pdfbookmarks for titles.
2119 if node
.parent
['ids']:
2120 for id in node
.parent
['ids']:
2121 self
.body
.append('\\hypertarget{%s}{}\n' % id)
2122 if not self
.use_latex_toc
:
2123 # BUG level depends on style. pdflatex allows level 0 to 3
2124 # ToC would be the only on level 0 so i choose to decrement the rest.
2125 # "Table of contents" bookmark to see the ToC. To avoid this
2126 # we set all zeroes to one.
2127 l
= self
.section_level
2130 # pdftex does not like "_" subscripts in titles
2131 text
= self
.encode(node
.astext())
2132 for id in node
.parent
['ids']:
2133 self
.body
.append('\\pdfbookmark[%d]{%s}{%s}\n' % \
2136 def visit_title(self
, node
):
2137 """Section and other titles."""
2139 if isinstance(node
.parent
, nodes
.topic
):
2140 # the table of contents.
2142 if ('contents' in self
.topic_classes
2143 and self
.use_latex_toc
):
2144 self
.body
.append('\\renewcommand{\\contentsname}{')
2145 self
.context
.append('}\n\\tableofcontents\n\n\\bigskip\n')
2146 elif ('abstract' in self
.topic_classes
2147 and self
.settings
.use_latex_abstract
):
2148 raise nodes
.SkipNode
2149 else: # or section titles before the table of contents.
2150 # BUG: latex chokes on center environment with
2151 # "perhaps a missing item", therefore we use hfill.
2152 self
.body
.append('\\subsubsection*{~\\hfill ')
2153 # the closing brace for subsection.
2154 self
.context
.append('\\hfill ~}\n')
2155 # TODO: for admonition titles before the first section
2156 # either specify every possible node or ... ?
2157 elif isinstance(node
.parent
, nodes
.sidebar
) \
2158 or isinstance(node
.parent
, nodes
.admonition
):
2159 self
.body
.append('\\textbf{\\large ')
2160 self
.context
.append('}\n\\smallskip\n')
2161 elif isinstance(node
.parent
, nodes
.table
):
2162 # caption must be written after column spec
2163 self
.active_table
.caption
= self
.encode(node
.astext())
2164 raise nodes
.SkipNode
2165 elif self
.section_level
== 0:
2167 self
.title
= self
.encode(node
.astext())
2168 if not self
.pdfinfo
== None:
2169 self
.pdfinfo
.append( 'pdftitle={%s}' % self
.encode(node
.astext()) )
2170 raise nodes
.SkipNode
2172 self
.body
.append('\n\n')
2173 self
.body
.append('%' + '_' * 75)
2174 self
.body
.append('\n\n')
2177 if self
.use_latex_toc
:
2182 section_name
= self
.d_class
.section(self
.section_level
)
2183 self
.body
.append('\\%s%s{' % (section_name
, section_star
))
2184 # MAYBE postfix paragraph and subparagraph with \leavemode to
2185 # ensure floatables stay in the section and text starts on a new line.
2186 self
.context
.append('}\n')
2188 def depart_title(self
, node
):
2189 self
.body
.append(self
.context
.pop())
2190 for id in node
.parent
['ids']:
2191 self
.body
.append('\\label{%s}\n' % id)
2193 def visit_topic(self
, node
):
2194 self
.topic_classes
= node
['classes']
2195 if ('abstract' in self
.topic_classes
2196 and self
.settings
.use_latex_abstract
):
2197 self
.body
.append('\\begin{abstract}\n')
2199 def depart_topic(self
, node
):
2200 if ('abstract' in self
.topic_classes
2201 and self
.settings
.use_latex_abstract
):
2202 self
.body
.append('\\end{abstract}\n')
2203 self
.topic_classes
= []
2204 if 'contents' in node
['classes'] and self
.use_latex_toc
:
2207 self
.body
.append('\n')
2209 def visit_inline(self
, node
): # titlereference
2210 classes
= node
.get('classes', ['Unknown', ])
2212 self
.body
.append( '\\docutilsrole%s{' % cls
)
2213 self
.context
.append('}'*len(classes
))
2215 def depart_inline(self
, node
):
2216 self
.body
.append(self
.context
.pop())
2218 def visit_rubric(self
, node
):
2219 self
.body
.append('\\rubric{')
2220 self
.context
.append('}\n')
2222 def depart_rubric(self
, node
):
2223 self
.body
.append(self
.context
.pop())
2225 def visit_transition(self
, node
):
2226 self
.body
.append('\n\n')
2227 self
.body
.append('%' + '_' * 75)
2228 self
.body
.append('\n\\hspace*{\\fill}\\hrulefill\\hspace*{\\fill}')
2229 self
.body
.append('\n\n')
2231 def depart_transition(self
, node
):
2234 def visit_version(self
, node
):
2235 self
.visit_docinfo_item(node
, 'version')
2237 def depart_version(self
, node
):
2238 self
.depart_docinfo_item(node
)
2240 def visit_warning(self
, node
):
2241 self
.visit_admonition(node
, 'warning')
2243 def depart_warning(self
, node
):
2244 self
.depart_admonition()
2246 def unimplemented_visit(self
, node
):
2247 raise NotImplementedError('visiting unimplemented node type: %s'
2248 % node
.__class
__.__name
__)
2250 # def unknown_visit(self, node):
2251 # def default_visit(self, node):
2253 # vim: set ts=4 et ai :