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