Add option ``--embed-stylesheet``.
[docutils.git] / docutils / writers / latex2e / __init__.py
blob0c0f4b6bc1341ed343c49c08a58d1322b7e8ee2e
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 a stylesheet file. The file will be "input" by latex in '
64 'the document header. Default is no stylesheet (""). '
65 'Overrides --stylesheet-path.',
66 ['--stylesheet'],
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 '
82 'sectionnumbers.',
83 ['--use-latex-toc'],
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 '
91 'document info.',
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 '
109 'the default.',
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'],
129 {'default': '', }),
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"',
139 ['--table-style'],
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'],
147 {'default': ''}),
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).',
153 ['--font-encoding'],
154 {'default': ''}),
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".',
162 ['--use-bibtex'],
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")
176 output = None
177 """Final translated form of `document`."""
179 def __init__(self):
180 writers.Writer.__init__(self)
181 self.translator_class = LaTeXTranslator
183 def translate(self):
184 visitor = self.translator_class(self.document)
185 self.document.walkabout(visitor)
186 self.output = visitor.astext()
187 # copy parts
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))
198 Notes on LaTeX
199 --------------
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.
207 * width
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
216 are not too wide.
218 So we add locallinewidth set it initially and
219 on entering sidebar and reset on exit.
222 class Babel:
223 """Language specifics for LaTeX."""
224 # country code by a.schlock.
225 # partly manually converted from iso and babel stuff, dialects and some
226 _ISO639_TO_BABEL = {
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
231 'sl': 'slovenian',
232 'af': 'afrikaans',
233 'bg': 'bulgarian',
234 'br': 'breton',
235 'ca': 'catalan',
236 'cs': 'czech',
237 'cy': 'welsh',
238 'da': 'danish',
239 'fr': 'french',
240 # french, francais, canadien, acadian
241 'de': 'ngerman', #XXX rather than german
242 # ngerman, naustrian, german, germanb, austrian
243 'el': 'greek',
244 'en': 'english',
245 # english, USenglish, american, UKenglish, british, canadian
246 'eo': 'esperanto',
247 'es': 'spanish',
248 'et': 'estonian',
249 'eu': 'basque',
250 'fi': 'finnish',
251 'ga': 'irish',
252 'gl': 'galician',
253 'he': 'hebrew',
254 'hr': 'croatian',
255 'hu': 'hungarian',
256 'is': 'icelandic',
257 'it': 'italian',
258 'la': 'latin',
259 'nl': 'dutch',
260 'pl': 'polish',
261 'pt': 'portuguese',
262 'ro': 'romanian',
263 'ru': 'russian',
264 'sk': 'slovak',
265 'sr': 'serbian',
266 'sv': 'swedish',
267 'tr': 'turkish',
268 'uk': 'ukrainian'
271 nobr = '~'
273 def __init__(self,lang):
274 self.language = 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`\"}'
284 else:
285 self.quotes = ("``", "''")
286 self.quote_index = 0
287 # for spanish ``~n`` must be ``~{}n``
288 if re.search('^es',self.language):
289 self.nobr = '~{}'
290 else:
291 self.nobr = '~'
293 def next_quote(self):
294 q = self.quotes[self.quote_index]
295 self.quote_index = (self.quote_index+1)%2
296 return q
298 def quote_quotes(self,text):
299 t = None
300 for part in text.split('"'):
301 if t == None:
302 t = part
303 else:
304 t += self.next_quote() + part
305 return t
307 def double_quotes_in_tt (self,text):
308 if not self.double_quote_replacment:
309 return text
310 return text.replace('"', self.double_quote_replacment)
312 def get_language(self):
313 if self.language in self._ISO639_TO_BABEL:
314 return self._ISO639_TO_BABEL[self.language]
315 else:
316 # support dialects.
317 lang = self.language.split("_")[0]
318 if lang in self._ISO639_TO_BABEL:
319 return self._ISO639_TO_BABEL[lang]
320 return None
322 latex_headings = {
323 'optionlist_environment' : [
324 '\\newcommand{\\optionlistlabel}[1]{\\bf #1 \\hfill}\n'
325 '\\newenvironment{optionlist}[1]\n'
326 '{\\begin{list}{}\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'
333 '}{\\end{list}}\n',
335 'lineblock_environment' : [
336 '\\newlength{\\lineblockindentation}\n'
337 '\\setlength{\\lineblockindentation}{2.5em}\n'
338 '\\newenvironment{lineblock}[1]\n'
339 '{\\begin{list}{}\n'
340 ' {\\setlength{\\partopsep}{\\parskip}\n'
341 ' \\addtolength{\\partopsep}{\\baselineskip}\n'
342 ' \\topsep0pt\\itemsep0.15\\baselineskip\\parsep0pt\n'
343 ' \\leftmargin#1}\n'
344 ' \\raggedright}\n'
345 '{\\end{list}}\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',
360 'some_commands' : [
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',
369 class DocumentClass:
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
378 document class.
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')
386 if self._with_part:
387 sections.insert(0, 'part')
388 if level <= len(sections):
389 return sections[level-1]
390 else:
391 return sections[-1]
393 class Table:
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.
398 Table style might be
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
408 self._open = 0
409 # miscellaneous attributes
410 self._attrs = {}
411 self._col_width = []
412 self._rowspan = []
413 self.stubs = []
414 self._in_thead = 0
416 def open(self):
417 self._open = 1
418 self._col_specs = []
419 self.caption = None
420 self._attrs = {}
421 self._in_head = 0 # maybe context with search
422 def close(self):
423 self._open = 0
424 self._col_specs = None
425 self.caption = None
426 self._attrs = {}
427 self.stubs = []
428 def is_open(self):
429 return self._open
431 def set_table_style(self, table_style):
432 if not table_style in ('standard','booktabs','borderless','nolines'):
433 return
434 self._table_style = table_style
436 def used_packages(self):
437 if self._table_style == 'booktabs':
438 return '\\usepackage{booktabs}\n'
439 return ''
440 def get_latex_type(self):
441 return self._latex_type
443 def set(self,attr,value):
444 self._attrs[attr] = value
445 def get(self,attr):
446 if attr in self._attrs:
447 return self._attrs[attr]
448 return None
449 def get_vertical_bar(self):
450 if self._table_style == 'standard':
451 return '|'
452 return ''
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'
458 else:
459 prefix = ''
460 return '%s\\begin{%s}[c]' % (prefix, self._latex_type)
461 def get_closing(self):
462 line = ""
463 if self._table_style == 'booktabs':
464 line = '\\bottomrule\n'
465 elif self._table_style == 'standard':
466 lines = '\\hline\n'
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.
481 === ===
482 ABC DEF
483 === ===
485 usually gets to narrow, therefore we add 1 (fiddlefactor).
487 width = 80
489 total_width = 0.0
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
494 self._col_width = []
495 self._rowspan = []
496 # donot make it full linewidth
497 factor = 0.93
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):
515 if not self.caption:
516 return ""
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()
524 return 0
526 def visit_thead(self):
527 self._in_thead += 1
528 if self._table_style == 'standard':
529 return ['\\hline\n']
530 elif self._table_style == 'booktabs':
531 return ['\\toprule\n']
532 return []
533 def depart_thead(self):
534 a = []
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')
542 else:
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
547 self._in_thead -= 1
548 return a
549 def visit_row(self):
550 self._cell_in_row = 0
551 def depart_row(self):
552 res = [' \\\\\n']
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':
559 rowspans = []
560 for i in range(len(self._rowspan)):
561 if (self._rowspan[i]<=0):
562 rowspans.append(i+1)
563 if len(rowspans)==len(self._rowspan):
564 res.append('\\hline\n')
565 else:
566 cline = ''
567 rowspans.reverse()
568 # TODO merge clines
569 while 1:
570 try:
571 c_start = rowspans.pop()
572 except:
573 break
574 cline += '\\cline{%d-%d}\n' % (c_start,c_start)
575 res.append(cline)
576 return res
578 def set_rowspan(self,cell,value):
579 try:
580 self._rowspan[cell] = value
581 except:
582 pass
583 def get_rowspan(self,cell):
584 try:
585 return self._rowspan[cell]
586 except:
587 return 0
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]
595 return False
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
604 # Templates
605 # ---------
607 latex_head = '\\documentclass[%s]{%s}\n'
608 linking = "\\ifthenelse{\\isundefined{\\hypersetup}}{\n" \
609 +"\\usepackage[colorlinks=%s,linkcolor=%s,urlcolor=%s]{hyperref}\n" \
610 +"}{}\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.
618 use_latex_toc = 0
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 = '-'
634 # default link color
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'
656 else:
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.
662 else:
663 self.bibtex = None
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'
683 else:
684 if self.d_options.find('DIV') == -1 and self.d_options.find('BCOR') == -1:
685 self.typearea = '\\typearea{12}\n'
686 else:
687 self.typearea = ''
689 if self.font_encoding == 'OT1':
690 fontenc_header = ''
691 elif self.font_encoding == '':
692 fontenc_header = '\\usepackage{ae}\n\\usepackage{aeguill}\n'
693 else:
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'
697 else:
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}',
706 '\\else',
707 ' \\usepackage[pdftex]{graphicx}',
708 '\\fi\n'))
709 else:
710 self.graphicx_package = (
711 '\\usepackage[%s]{graphicx}\n' % self.settings.graphicx_option)
713 self.head_prefix = [
714 self.latex_head % (self.d_options,self.settings.documentclass),
715 '\\usepackage{babel}\n', # language is in documents settings.
716 fontenc_header,
717 '\\usepackage{shortvrb}\n', # allows verb in footnotes.
718 input_encoding,
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.
724 # * fancyhdr
725 # * ltxtable is a combination of tabularx and longtable (pagebreaks).
726 # but ??
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!
735 self.typearea,
736 self.generator,
737 # latex lengths
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',
747 # will be set later.
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)
755 if stylesheet:
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())
761 else:
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
772 self.pdfinfo = [ ]
773 self.pdfauthor = None
774 # pdftitle, pdfsubject, pdfauthor, pdfkeywords,
775 # pdfcreator, pdfproducer
776 else:
777 self.pdfinfo = None
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.
783 self.head = [ ]
784 # separate title, so we can appen subtitle.
785 self.title = ''
786 # if use_latex_docinfo: collects lists of author/organization/contact/address lines
787 self.author_stack = []
788 self.date = ''
790 self.body_prefix = ['\\raggedbottom\n']
791 self.body = []
792 self.body_suffix = ['\n']
793 self.section_level = 0
794 self.context = []
795 self.topic_classes = []
796 # column specification for tables
797 self.table_caption = None
799 # Flags to encode
800 # ---------------
801 # verbatim: to tell encode not to encode.
802 self.verbatim = 0
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
826 self._bibitems = []
828 # docinfo.
829 self.docinfo = None
830 # inside literal block: no quote mangling.
831 self.literal_block = 0
832 self.literal_block_stack = []
833 self.literal = 0
834 # true when encoding in math mode
835 self.mathmode = 0
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
859 #"": "applemac",
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
865 #"": "decmulti",
866 #"": "latin10",
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 = {
881 u'\u00A0' : '~',
882 u'\u2013' : '{--}',
883 u'\u2014' : '{---}',
884 u'\u2018' : '`',
885 u'\u2019' : '\'',
886 u'\u201A' : ',',
887 u'\u201C' : '``',
888 u'\u201D' : '\'\'',
889 u'\u201E' : ',,',
890 u'\u2020' : '{\\dag}',
891 u'\u2021' : '{\\ddag}',
892 u'\u2026' : '{\\dots}',
893 u'\u2122' : '{\\texttrademark}',
894 u'\u21d4' : '{$\\Leftrightarrow$}',
895 # greek alphabet ?
898 def unicode_to_latex(self,text):
899 # see LaTeX codec
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])
905 return text
907 def ensure_math(self, text):
908 if not 'ensure_math_re' in self.__dict__:
909 chars = {
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)
916 return 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.
926 # AND quotes
927 if self.verbatim:
928 return text
929 # compile the regexps once. do it here so one can see them.
931 # first the braces.
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)
942 # then dollar
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}')
950 # then
951 text = text.replace("&", '{\\&}')
952 # the ^:
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.)
962 separate_chars = '-'
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
966 # non-literal text.
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
973 # correct width.
974 text = text.replace('_', '{\\underline{ }}')
975 # And the tt-backslash doesn't work in OT1, so we use
976 # a mirrored slash.
977 text = text.replace('\\textbackslash', '\\reflectbox{/}')
978 else:
979 text = text.replace('_', '{\\_}')
980 else:
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
986 # "---" by "-{}--".
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)
997 else:
998 closings = ""
999 openings = ""
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)
1007 return text
1009 def literal_block_env(self, begin_or_end):
1010 env = 'verbatim'
1011 opt = ''
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))
1026 def astext(self):
1027 if self.pdfinfo is not None and self.pdfauthor:
1028 self.pdfinfo.append('pdfauthor={%s}' % self.pdfauthor)
1029 if self.pdfinfo:
1030 pdfinfo = '\\hypersetup{\n' + ',\n'.join(self.pdfinfo) + '\n}\n'
1031 else:
1032 pdfinfo = ''
1033 head = '\\title{%s}\n\\author{%s}\n\\date{%s}\n' % \
1034 (self.title,
1035 ' \\and\n'.join(['~\\\\\n'.join(author_lines)
1036 for author_lines in self.author_stack]),
1037 self.date)
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):
1045 pass
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')
1056 if name:
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.
1079 pass
1081 def depart_authors(self, node):
1082 pass
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' )
1095 else:
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' )
1101 else:
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('$^{')
1108 self.mathmode = 1
1110 def depart_superscript(self, node):
1111 self.body.append('}$')
1112 self.mathmode = 0
1114 def visit_subscript(self, node):
1115 self.body.append('$_{')
1116 self.mathmode = 1
1118 def depart_subscript(self, node):
1119 self.body.append('}$')
1120 self.mathmode = 0
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))
1144 else:
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])
1156 else:
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
1164 else:
1165 assert self.body[-1] in (' ', '\n'),\
1166 'unexpected non-whitespace while in reference label'
1167 del self.body[-1]
1168 else:
1169 href = ''
1170 if 'refid' in node:
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(',')
1189 else:
1190 self.body.append('}')
1191 self.inside_citation_reference_label = 0
1192 else:
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):
1205 pass
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):
1213 pass
1215 def depart_compound(self, node):
1216 pass
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):
1225 pass
1227 def depart_container(self, node):
1228 pass
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):
1249 pass
1251 def depart_decoration(self, node):
1252 pass
1254 def visit_definition(self, node):
1255 pass
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):
1267 pass
1269 def depart_definition_list_item(self, node):
1270 pass
1272 def visit_description(self, node):
1273 self.body.append( ' ' )
1275 def depart_description(self, node):
1276 pass
1278 def visit_docinfo(self, node):
1279 self.docinfo = []
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.
1289 self.docinfo = None
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())
1296 else:
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
1309 else:
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')
1321 else:
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:]
1332 dest.extend(tail)
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}' )
1338 self.verbatim = 1
1340 def depart_doctest_block(self, node):
1341 self.body.append( '\\end{verbatim}\n' )
1342 self.verbatim = 0
1344 def visit_document(self, node):
1345 self.body_prefix.append('\\begin{document}\n')
1346 # titled document?
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.
1350 # \begin{titlepage}
1351 # ...
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:
1357 if not self.bibtex:
1358 widest_label = ""
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')
1368 else:
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()
1384 # cell separation
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):
1389 count = 0
1390 while self.active_table.get_rowspan(count):
1391 count += 1
1392 self.body.append(' & ')
1393 self.active_table.visit_entry() # increment cell count
1394 else:
1395 self.body.append(' & ')
1397 # multi{row,column}
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()
1416 else:
1417 bar1 = ''
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('}')
1422 else:
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('}')
1432 else:
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' }
1458 enum_suffix = ""
1459 if 'suffix' in node:
1460 enum_suffix = node['suffix']
1461 enum_prefix = ""
1462 if 'prefix' in node:
1463 enum_prefix = node['prefix']
1464 if self.compound_enumerators:
1465 pref = ""
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
1470 enum_prefix += pref
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)
1486 else:
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.
1494 if 'start' in node:
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
1513 pass
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.
1527 if self.docinfo:
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
1547 if self.docinfo:
1548 self.docinfo.append('\\textbf{%s}: &\n\t' % self.encode(node.astext()))
1549 raise nodes.SkipNode
1550 else:
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'
1562 align_end = ''
1563 else:
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('{')
1589 else:
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')
1597 else:
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
1604 href = ''
1605 if 'refid' in node:
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':
1611 suffix = '['
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:
1622 return
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)
1632 else:
1633 self.body.append(superscript)
1634 else:
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):
1647 pass
1649 def depart_generated(self, node):
1650 pass
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)
1670 if not match:
1671 # fallback
1672 return width_str
1673 res = width_str
1674 amount, unit = match.groups()[:2]
1675 if unit == "px":
1676 # LaTeX does not know pixels but points
1677 res = "%spt" % amount
1678 elif unit == "%":
1679 res = "%.3f\\linewidth" % (float(amount)/100.0)
1680 return res
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
1688 post = []
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,))
1695 post.append('}')
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:
1703 align_prepost = {
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', '}'),}
1714 try:
1715 pre.append(align_prepost[inline, attrs['align']][0])
1716 post.append(align_prepost[inline, attrs['align']][1])
1717 except KeyError:
1718 pass # XXX complain here?
1719 if not inline:
1720 pre.append('\n')
1721 post.append('\n')
1722 pre.reverse()
1723 self.body.extend( pre )
1724 options = ''
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):
1732 pass
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')
1764 else:
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):
1779 self.literal = 1
1780 self.body.append('\\texttt{')
1782 def depart_literal(self, node):
1783 self.body.append('}')
1784 self.literal = 0
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')
1810 else:
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)):
1816 self.verbatim = 1
1817 self.body.append(self.literal_block_env('begin'))
1818 else:
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):
1826 if self.verbatim:
1827 self.body.append(self.literal_block_env('end'))
1828 self.verbatim = 0
1829 else:
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):
1864 pass
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):
1882 pass
1884 def depart_option_list_item(self, node):
1885 pass
1887 def visit_option_string(self, node):
1888 ##self.body.append(self.starttag(node, 'span', '', CLASS='option'))
1889 pass
1891 def depart_option_string(self, node):
1892 ##self.body.append('</span>')
1893 pass
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
1905 index > 0 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 '#'.
1927 hash_char = '\\#'
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']]
1934 else:
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': ('(', ')'),
1978 'none': ('', '')}
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):
2026 pass
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()
2046 else:
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('}')
2059 else:
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):
2073 pass
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')
2086 pass
2088 def depart_tgroup(self, node):
2089 pass
2091 _thead_depth = 0
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
2128 if l>0:
2129 l = l-1
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' % \
2134 (l, text, id))
2136 def visit_title(self, node):
2137 """Section and other titles."""
2139 if isinstance(node.parent, nodes.topic):
2140 # the table of contents.
2141 self.bookmark(node)
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:
2166 # document title
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
2171 else:
2172 self.body.append('\n\n')
2173 self.body.append('%' + '_' * 75)
2174 self.body.append('\n\n')
2175 self.bookmark(node)
2177 if self.use_latex_toc:
2178 section_star = ""
2179 else:
2180 section_star = "*"
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:
2205 pass
2206 else:
2207 self.body.append('\n')
2209 def visit_inline(self, node): # titlereference
2210 classes = node.get('classes', ['Unknown', ])
2211 for cls in classes:
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):
2232 pass
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 :